Using ccTouchBegan and ccTouchesEnded at the same time in Cocos2dx - cocos2d-x

Can I use ccTouchBegan and ccTouchesEnded at the same time?
When I'm trying to add actions in ccTouchBegan, I recognize that ccTouchesEnded isn't called. Can anyone explain for me how to use touch events?
Some of my code
bool GameplayScene::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent){
CCActionInterval* actionBy = CCRotateBy::create(1, 180);
weapon->runAction(CCSequence::create(actionBy, NULL, NULL));
location = pTouch->getLocation();
shootBullet();
this->schedule(schedule_selector(GameplayScene::shootBullet), 1.0);
return true;
}
void GameplayScene::ccTouchesEnded(cocos2d::CCSet* touches, cocos2d::CCEvent* event){
this->unschedule(schedule_selector(GameplayScene::shootBullet));
}
void GameplayScene::registerWithTouchDispatcher(void) {
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, INT_MIN + 1, true);
}

First you are using ccTouchBegan and ccTouchesEnded, you could use ccTouchesBegan.
To use ccTouchesBegan and ccTouchesEnded, you need to set:
layer->setTouchEnabled(true);
where layer is the main layer of your Gameplay scene.

Related

Functions not doing anything

I have a gameObject called BounceBack that is supposed to bounce the ball back far away when they collide together.
public class BounceBack : MonoBehaviour
{
public GameObject Player;
public float force;
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag(Player.tag))
{
Player.GetComponent<PlayerController>().ForceBack(force);
}
}
}
The ball Player (ball) script:
public class PlayerController : MonoBehaviour
{
public int acceleration;
public int speedLimit;
public int sideSpeed;
public Text countText;
public Text winText;
public GameObject pickUp;
public GameObject finishLine;
//internal void ForceBack() //Not sure what it does and why it's there.
//{
// throw new NotImplementedException();
//}
private int count;
private Rigidbody rb;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
SetCount();
}
// Update is called once per frame
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal") * sideSpeed * rb.velocity.magnitude / acceleration;
//float moveVertical = Input.GetAxis("Vertical") * acceleration;
if (rb.velocity.magnitude <= speedLimit)
{
rb.AddForce(0.0f, 0.0f, acceleration); // add vertical force
}
rb.AddForce(moveHorizontal, 0.0f, 0.0f); // add horizontal force
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag(pickUp.tag))
{
other.GetComponent<Rotate>().Disapear();
count++;
SetCount();
}
if (other.gameObject.CompareTag(finishLine.tag))
{
acceleration = 0;
sideSpeed = 0;
finishLine.GetComponent<GameEnd>().FadeOut();
if (count >= 2)
{
winText.GetComponent<WinTextFadeIn>().FadeIn("Vous avez remporté la partie!");
}
else
{
winText.GetComponent<WinTextFadeIn>().FadeIn("Vous avez perdu. Réesayer?");
}
}
}
private void SetCount()
{
countText.text = "Count : " + count.ToString();
}
public void ForceBack(float force)
{
Rigidbody rb = GetComponent<Rigidbody>();
rb.AddForce(0.0f, 0.0f, -force, ForceMode.VelocityChange);
Debug.Log("Pass");
}
}
The AddForce function does not do anything. I tried with setActive(false) and it's not working either. The only thing that works is Debug.Log(). I'm not sure if the speedlimit and acceleration are interfering with the function.
EDIT: I'm not sure if the problem is from Unity but I can't access any variable of the class from the forceBack function inside the class.
EDIT2: I also tried to call the AddForce function directly in the Bounce Back script but it's not working either.
Player.GetComponent<Rigidbody>().AddForce(0.0f, 0.0f, -force, ForceMode.VelocityChange);
Player (Ball) Screenshot
Bounce Back Screenshot
So, a couple things:
1.) The physics system should already cause the ball to bounce if you've set up the colliders and rigidbodies properly. You should only need to do something like this if the ball should gain momentum when it bounces, which is unlikely. You should post screenshots of their inspectors if this answer doesn't help.
2.) On your rb.AddForce() call, you're applying a force in world-space, which may be the wrong direction to bounce. If you know the ball is oriented the way it's moving, then you can call AddRelativeForce with the same parameters. If the ball's orientation is not controlled, then you need to calculate the correct world-space direction to use before applying the force.
3.) Finally, just to confirm, the objects with BounceBack attached do have a non-zero value in the 'force' parameter in the inspector, right?

cocos2d-x 3.6 PhysicsJointFixed error

I am new to cococs2dx and I am working on a code involving PhysicsJointFixed. I am using cocos2d-x-3.6. I am unable to compile the code as I wrote below following the guidelines and the PhysicsTest.cpp.
My GameLayer.h looks like:
class GameLayer : public cocos2d::Layer
{
GameLayer();
virtual ~GameLayer();
virtual bool init();
static Scene* createScene();
void setPhyWorld(PhysicsWorld* world){m_world = world;}
CREATE_FUNC(GameLayer);
private:
PhysicsWorld* m_world;
ShapeSprite* _square; //ShapeSprite extends Sprite
ShapeSprite* _square1;
PhysicsJointFixed* joint;
...
}
The createScene method in GameLayer.cpp:
Scene* GameLayer::createScene()
{
auto scene = Scene::createWithPhysics();
auto layer = GameLayer::create();
layer->setPhyWorld(scene->getPhysicsWorld());
scene->addChild(layer);
return scene;
}
Then inside GameLayer::init()
bool GameLayer::init()
{
if ( !Layer::init() )
{
return false;
}
...
...
_square = ShapeSprite::gameSpriteWithFile("square.png");
auto squareBody = PhysicsBody::createBox(Size(200,200));
_square->setPhysicsBody(squareBody);
_square->setPosition(Vec2(_screenSize.width * 0.5f, _screenSize.height * 0.7f));
_square1 = ShapeSprite::gameSpriteWithFile("square1.png");
auto squareBody1 = PhysicsBody::createBox(Size(100,100));
_square1->setPhysicsBody(squareBody1);
_square1->setPosition(Vec2(_screenSize.width * 0.5f, _screenSize.height * 0.7f));
this->addChild(_square);
this->addChild(_square1);
joint = PhysicsJointFixed::construct(_square->getPhysicsBody(), _square1->getPhysicsBody(),Vec2(100,100));
this->getScene()->getPhysicsWorld()->addJoint(joint);
return true;
}
The code gives EXC_BAD_ACCESS error on the line
this->getScene()->getPhysicsWorld()->addJoint(joint);
because, this->getScene()->getPhysicsWorld() returns NULL.
Please advise, how can I avoid the error. Any suggestion is appreciated. Thanks in advance.
This error occurs because your custom layer is not yet added to scene during init(). One possible solution is to override onEnterTransitionDidFinish() and add the joint there. In GameLayer.h add this:
virtual void onEnterTransitionDidFinish();
And move the joint adding code to GameLayer.cpp like this:
void GameLayer::onEnterTransitionDidFinish() {
joint = PhysicsJointFixed::construct(_square->getPhysicsBody(), _square1->getPhysicsBody(),Vec2(100,100));
this->getScene()->getPhysicsWorld()->addJoint(joint);
}

How to disable multitouch in Android in cocos2d-x 3.2

I am setting handlers for single touch in this way
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->setSwallowTouches(true);
touchListener->onTouchBegan = CC_CALLBACK_2(MyClass::onTouchBegan, this);
touchListener->onTouchMoved = CC_CALLBACK_2(MyClass::onTouchMoved, this);
touchListener->onTouchEnded = CC_CALLBACK_2(MyClass::onTouchEnded, this);
auto dispatcher = Director::getInstance()->getEventDispatcher();
dispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
For iOS it works properly, but for Android if I touch the screen with two fingers in the same time it will call onTouchBegan twice.
How can I disable multitouch from cocos2d-x (3.2) code for Android?
I found a workaround, since cocos2d-x doesn't have an official solution for this. (using Cocos2d-x 3.2)
Since each touch has it's own ID, we just need to filter first touch ID from others.
I have achieved this in the following way:
Created layer's instance variable:
int _currentTouchID;
Initialised it with -1 in layer's init() method:
_currentTouchID = -1;
In the beginign of all touch handlers I did next:
bool MyClass::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *event)
{
if (_currentTouchID < 0) {
_currentTouchID = touch->getID();
}
else {
return false;
}
//Your code here
return true;
}
void MyClass::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event)
{
if (_currentTouchID != touch->getID()) {
return;
}
//Your code here
}
void MyClass::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event)
{
if (_currentTouchID == touch->getID()) {
_currentTouchID = -1;
}
else {
return;
}
//Your code here
}
That's it. Please provide your solution if you have found a better one.
BTW: Commenting the switch case MotionEvent.ACTION_POINTER_DOWN: in Cocos2dxGLSurfaceView.java file,
as it was offered on cocos2d-x forum, didn't work for me.

How to perform drag and drop operation on sprite fetched from array and present on screen ?

I want to perform drag and drop operation on these image.
How can i make it possible with the following code.
void storeLocation::changescene()
{
this->removeAllChildren();
//CCDirector::sharedDirector()->replaceScene(storeLocation::scene());
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
//CCScene* scene=CCScene::create();
storeLocation *layer = storeLocation::create();
CCSprite *k=CCSprite::create("background.png");
this->addChild(k,0);
k->setPosition(ccp(visibleSize.width/2+ origin.x, visibleSize.height/2 + origin.y));
CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(storeLocation::menuCloseCallback));
pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width ,
origin.y + pCloseItem->getContentSize().height/2));
pCloseItem->setScale(1.5);
// create menu, it's an autorelease object
CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
pMenu->setPosition(CCPointZero);
this->addChild(pMenu, 1);
this->addChild(pMenu, 1);
this->setTouchEnabled(true);
int l=5;
int posx=0,posy=0;
int count=0,r,j=-1,i=0,flag=0;
int x=20;
int b[30],a[30];
while(count<=5)
{
srand(time(0));
r=rand()%x+1;
flag=checktag(b,r,j);
if(flag==1)
{
b[i]=r;
i++;
count++;
j++;
}
}
int t;
CCObject* jt=NULL;
CCARRAY_FOREACH(images, jt)
{
// CCSize winSize = CCDirector::sharedDirector()->getWinSize();
//float i=winSize.width;
CCSprite *image = dynamic_cast<CCSprite*>(jt);
t=image->getTag();
for(i=0;i<l;i++)
{
if(t==b[i])
{
this->addChild(image);
image->setPosition(ccp(100+posx,100));
posx=posx+120;
}}}
To drag and drop images from one point to another on screen you have to use touch delegate methods
bool ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
void ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
void ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
Detect image on user touch in ccTouchBegan method, for this you can store all image objects in a array and check if touch is in rect of any image by using for loop.
To move the image with user touch change position of touched image(save touched image object in a global object) in ccTouchMoved.
And in ccTouchEnded method do whatever you want to do on droping image.
the easiest way to catch drag and drop events is by implementing the onTouchBegan and onTouchMoved and onTouchEnded methods like this:
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, sprite);
bool HelloWorld::onTouchBegan(Touch* touch, cocos2d::Event* event){
// this method is not needed but in order to implement the onTouchMoved you have to first implement onTouchBegan then the onTouchMoved
return true;
}
void HelloWorld::onTouchMoved(Touch* touch, cocos2d::Event* event){
if (sprite->getBoundingBox().containsPoint(touch->getLocation()))
{
sprite->setPosition(sprite->getPosition() + touch->getDelta());
}
}
void HelloWorld::onTouchEnded(Touch* touch, cocos2d::Event* event){
if (sprite->getBoundingBox().containsPoint(touch->getLocation()))
{
log("Sprite Drop Event");
}
}

Difference between CCLayer::init() and CCLayer::onEnter()?

Here is the topic on Difference between CCNode::init() and CCNode::onEnter(). However I followed the advice they gave.
void MyLayer::onEnter() {
CCLayer::onEnter();
//your code goes here
}
I got the Assertion failed! error!
MyLayer code:
class MyLayer : public CCLayerColor
Should I add CCLayerColor::onEnter() in my MyLayer::onEnter() code? And what is the difference between CCLayer::init() and CCLayer::onEnter(). Which part of code should I put into init() and which part should put into onEnter()?
Cocos2d-x has its memory allocation model as a two-step process, like objective-c. Each object has memory allocated (usually using a "create" method) and then has its state initialized (usually using a method called "init"). So in the create/init methods, you allocate the memory and do any object initialization necessary for it to run.
When the object starts to be put onto the display, or when it is added to another container, its "onEnter" method is called. This gets called when a CCScene/CCLayer (either of which may be containing your CCNode derived object) is displayed by the framework itself.
There are at least 2 patterns for memory allocation and object creation, I tend to follow the pattern of having a class contain a static factory method and a private constructor so that it is unambiguous that you must create the objects through the factory and cannot create one yourself.
For example, I am currently working on this "button" class:
class ActionButton;
class ActionButtonTarget
{
public:
virtual void ActionButtonActivated(ActionButton* button) = 0;
};
class ActionButton : public CCNode, public CCTargetedTouchDelegate
{
private:
ActionButton();
CCNode* _node; // Weak Reference
CCRect _testRect;
ActionButtonTarget* _target;
bool init(ActionButtonTarget* target, CCNode* node, CCRect rect);
bool IsTouchInside(CCTouch* touch);
void NotifyTarget();
protected:
virtual CCAction* CreateAction();
public:
virtual ~ActionButton();
// The class registers/unregisters on entry
// or exit of the layer. This
virtual void onEnterTransitionDidFinish();
virtual void onExitTransitionDidStart();
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);
static ActionButton* create(ActionButtonTarget* target, CCNode* node, CCRect rect);
};
Note that the "create" method is the only way to create it. In this case, it takes arguments for parameters of the button. Other CCNode derived objects (e.g. CCScene) usually do not.
Internally:
ActionButton::ActionButton()
{
}
ActionButton::~ActionButton()
{
}
// The class registers/unregisters on entry
// or exit of the layer. This
void ActionButton::onEnterTransitionDidFinish()
{
CCNode::onEnterTransitionDidFinish();
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
}
void ActionButton::onExitTransitionDidStart()
{
CCNode::onExitTransitionDidStart();
CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
}
bool ActionButton::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
if(IsTouchInside(pTouch))
{
return true;
}
return false;
}
void ActionButton::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
{
// Nothing to do here.
}
void ActionButton::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
{
NotifyTarget();
}
bool ActionButton::IsTouchInside(CCTouch* touch)
{
CCPoint point = touch->getLocationInView();
point = CCDirector::sharedDirector()->convertToGL(point);
return _testRect.containsPoint(point);
}
void ActionButton::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent)
{
_target->ActionButtonActivated(this);
}
ActionButton* ActionButton::create(ActionButtonTarget* target, CCNode* node, CCRect rect)
{
ActionButton *pRet = new ActionButton();
if (pRet && pRet->init(target,node,rect))
{
pRet->autorelease();
return pRet;
}
else
{
CC_SAFE_DELETE(pRet);
return NULL;
}
}
CCAction* ActionButton::CreateAction()
{
return NULL;
}
void ActionButton::NotifyTarget()
{
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect(Constants::SOUND_EFFECT_BUTTON_CLICK());
_target->ActionButtonActivated(this);
}
bool ActionButton::init(ActionButtonTarget* target, CCNode* node, CCRect rect)
{
assert(target != NULL);
assert(node != NULL);
assert(dynamic_cast<ActionButtonTarget*>(target) != NULL);
_target = target;
_node = node;
_testRect = rect;
addChild(_node);
return true;
}
Note the OnEnterXXX and onExitXXX methods call the parent class method. YOU MUST DO THIS or it will not work as expected.
This class is a button that will run an action on the node (maybe make it grow/shrink to indicate it got pressed). It takes touches from the user. I cannot add it to the touch manager before it has entered the scene. So I use the onEnterTransitionDidFinish method to add it and also use the onExitTransitionDidStart to remove it so it will not continue to receive touches after the scene has been removed. I don't know if the container will be destroyed or not, so I must remove it when the button exits the display.
Was this helpful?
Ist Part
Check out OnEnter method of base class(CCLayer),if some serious stuff is going on ,then you should have called it.
IInd Part
As in the topic... OnEnter is called when "viewDidAppear" and init during initialisation.
Suppose you create layer by this
MyLayer *newLayer=MyLayer::create(); //...init is called before OnEnter..
After that you add it to some scene
X->addChild(newLayer); // ...*Now onEnter Method is called
*X must be added to some scene or parent and so on.
IIIrd Part
In some case you just create multiple objects stored in array,but didn't add them. You are waiting for right time for adding them.This might be case when object enter/add to screen ,you want it to perform specific task.
Ex. In a game you have to attack someone and have 30 troops.All the troops are created in loading scene but none of them add to layer.
When battle begins, the deployed troop by user appear on the screen.Troop on enter has a task to go to target building and that method can be called through onEnter.
You call onEnter() if you want to do something the moment it appears on the screen.
The init() method will be called even without the layer being on the screen.
Something like this:
void FirstScene::methodInit()
{
customlayer = new CustomLayer();
customlayer -> init();
customlayer-> retain();
// At this point,customlayer (which is a CustomLayer object that is a subclass of CCLayer)
// is still not on the screen, hence, CustomLayer::onEnter() is still not called
}
void FirstScene::methodEnter
{
this -> addChild( customLayer, customIndex, customTag );
customLayer -> release();
// At this point, CustomLayer::onEnter() is called, because customLayer is being rendered
}