Why can't getCursorX() get the value beyond the offscreen area?

When the cursor clicks this position, the node moves here, no problem,the picture and code as follows:

void HelloWorld::onMouseDown(Event* event){
		node3_3=Sprite::create("ty.png");
		node3_3->setPosition(node2->getPosition());
		this->addChild(node3_3);
		this->p_3=(EventMouse*)event;	
		auto moveTo = MoveTo::create(1, Vec2(p_3->getCursorX(),p_3->getCursorY()));
		node3_3->runAction(moveTo);
}

Then the node moves to the right,
After the screen moves with the node for a certain distance, the cursor stays on the red circle and clicks,the picture and code as follows:

bool HelloWorld::init(){
	auto s = Director::getInstance()->getWinSize();
	auto pFollow=Follow::create(node2,Rect(0, 0, s.width+480, s.height));
	this->runAction(pFollow);
}
void HelloWorld::onMouseDown(Event* event){
		node3_3=Sprite::create("ty.png");
		node3_3->setPosition(node2->getPosition());
		this->addChild(node3_3);
		this->p_3=(EventMouse*)event;	
		auto moveTo = MoveTo::create(1, Vec2(p_3->getCursorX(),p_3->getCursorY()));
		node3_3->runAction(moveTo);
}

Why can’t the node3_3 node move to the position of the red circle where the cursor is?How to resolve this problem?

maybe you need an offset (s.width+480)?
p_3->getCursorX()+offset

it can’t serve that purpose。

I’m fairly certain you need to convert from Screen → World coordinates. It looks like you’re moving the position of the scene/layer (or root node) origin to some coord {-x,0}. Let’s say you’ve moved it to {-300,0} based on your screenshots. Then that when you click you get screen coords of, let’s say, {900, 200} based on the screenshot assuming the window/screen width/height is 1000x800. So when you tell the node to move to the position {900,200} it shows up in the screen at {900-300,200-0} or {600,200}.

You may have to play with positioning for a bit, and you’ll probably want to read the internals of the Follow action (CCFollow.cpp or whatever its filename is) to understand it better.

See documentation or source code for methods with names something like NodeToWorldSpace, NodeToScreenSpace, or maybe ScreenToWorld and vice versa, or similar. Should be methods on the Node class. I don’t have the source on this computer at the moment.

Quick search:

It seems that the convertToWorldSpace function needs to be used,
I try to convert node3_3 node and cursor position to position in world coordinate system,
But after running it can not achieve the expected effect.
The code as follows:

void HelloWorld::onMouseDown(Event* event){
		node3_3=Sprite::create("ty.png");
		node3_3->setPosition(node2->getPosition());
		Vec2 pos1=node3_3->convertToWorldSpace(Vec2::ZERO);
		node3_3->setPosition(pos1);
		this->addChild(node3_3);
		SimpleAudioEngine::getInstance()->playEffect("yingxiao.mp3", false, 1.0, 1.0, 1.0);
		this->p_3=(EventMouse*)event;	
		Vec2 pos=node3_3->convertToWorldSpace(Vec2(p_3->getCursorX(),p_3->getCursorY()));
		auto moveTo = MoveTo::create(1, Vec2(pos)); 
		node3_3->runAction(moveTo);
}

Can you share your entire HelloWorld.cpp and HelloWorld.h?
Just to make it easy to quickly create a test project.

For testing, it’s always good to get rid of (or comment out to keep it for later) all unnecessary code. Once you get it working you can add the code back into the test (uncomment or whatever).

So, I’d suggest is get rid of the MoveTo::create() and then the only thing you should do in MouseDown is create the sprite and then set the position of that sprite to the mouse location converted to world space

void HelloWorld::onMouseDown(Event *event) {
    auto eventMouse = (EventMouse *)event;
    Vec2 mouseLocation = Vec2(eventMouse->getCursorX(), eventMouse->getCursorY());

    // NOTE: it's fine to create node every click for testing, but
    // if you only want one sprite that you move every click then
    // eventually using the class instance field node3_3 instead
    // is smart to avoid having to delete the temporary node

    // HOWEVER: you wouldn't want to create the node in here
    // but rather do so in ::init() or in some other one-time startup code

    // create and add node to the world (this is root layer)
    auto node = Sprite::create("ty.png");
    this->addChild(node);

    // convert mouse (screen) coord to world space (of the game root layer node)
    Vec2 worldLocation = this->convertToWorldSpace(mouseLocation);
    node->setPosition(worldLocation);
}

could also do other tests:


    // Test using node2 to convert to world space ... I think it'll be the 
    // same as using `this` because I believe convertToWorldSpace 
    // converts the Vec2 transformed through all parent nodes, but I 
    // could be wrong?

    // (replace last 2-lines above to test convert off `node2` instead)
    Vec2 worldLocationOfNode = node2->convertToWorldSpace(mouseLocation);
    node->setPosition(worldLocationOfNode);
1 Like

Changed the previous error, this is the new test code, but after running the problem in the picture.When out of screen area.The picture does not appear at the cursor click position.
the .h file as follows:

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
using namespace cocos2d;
class HelloWorld : public cocos2d::Scene
{
private:
	cocos2d::Sprite* node5;
	cocos2d::Sprite* node2;
	Scheduler* _scheduler1_3;
	EventListenerMouse* _mouseListener_3;
	EventListenerMouse* _mouseListener1_3;
	EventDispatcher* _eventDispatcher_3;
	EventMouse* p_3;

public:
    static cocos2d::Scene* createScene();
    virtual bool init();
    void menuCloseCallback(cocos2d::Ref* pSender);
	HelloWorld::HelloWorld(){
		node5=nullptr;
		node2=nullptr;
		_scheduler1_3=Director::getInstance()->getScheduler();
		_mouseListener_3=EventListenerMouse::create();
		_mouseListener1_3=EventListenerMouse::create();
		_eventDispatcher_3 = _director->getEventDispatcher();
	}
    CREATE_FUNC(HelloWorld);
	void onMouseDown(cocos2d::Event* event);
};

#endif // __HELLOWORLD_SCENE_H__

the .ccp code as follows:

#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
#include "cocos2d.h"
#include<cmath> 
#include<stdlib.h> 
#include "AppDelegate.h"

using namespace cocos2d;
using namespace CocosDenshion;

USING_NS_CC;

Scene* HelloWorld::createScene()
{
	return HelloWorld::create();
}

// Print useful error message instead of segfaulting when files are not there.
static void problemLoading(const char* filename)
{
	printf("Error while loading: %s\n", filename);
	printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in HelloWorldScene.cpp\n");
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
	// 1. super init first
	if (!Scene::init())
	{
		return false;
	}


	this->_mouseListener_3->onMouseDown=CC_CALLBACK_1(HelloWorld::onMouseDown,this);
	_eventDispatcher_3->addEventListenerWithSceneGraphPriority(_mouseListener_3,this);

	this->node2=Sprite::create("ty.png");
	node2->setPosition(Vec2(750,160));   
	this->addChild(node2,5);

	auto s = Director::getInstance()->getWinSize();
	this->node5=Sprite::create("q.png");
	node5->setPosition(Vec2(1600,160));    
	this->addChild(node5,1);
	auto pFollow=Follow::create(node2,Rect(0, 0, s.width+480, s.height));
	this->runAction(pFollow);

	auto moveTo = MoveTo::create(8, Vec2(1555,160)); 
	node2->runAction(moveTo);

	return true;
}

void HelloWorld::onMouseDown(Event* event){
    auto eventMouse = (EventMouse *)event;
    Vec2 mouseLocation = Vec2(eventMouse->getCursorX(), eventMouse->getCursorY());
	auto node = Sprite::create("ty.png");
    this->addChild(node,3);
    Vec2 worldLocation = this->convertToWorldSpace(mouseLocation);
    node->setPosition(worldLocation);
}

the picture as follows:

It looks like it needs to be converted to screen coordinates, not to world coordinates, because after conversion to world coordinates, the sprite cannot appear in the off-screen area when the cursor is clicked.
The demo file as follows:
demo.zip (1.2 MB)

I’ll try and take a look at the demo project this week.

My advice was more about possible solutions and things to test.

Basically the solution will end up being whenever you get a coordinate from the mouse that corresponds to clicking at e.g. {500,200} within an 800x600 window (adjust for your actual numbers) you need to place it on the background (ie: the world) at {800x200} instead of {500x200} because the world has moved 300 to the left (the window lower-left corner would be at {300,0}).

Therefore if those numbers were accurate whatever calculation (world, node, screen, etc space conversions) basically is quite easily determined as {mouseX + 300, mouseY}

Anyway, I’ll let you know what code should change for the demo.zip to work.

After checking out the demo test code I think my main advice would be to read up somewhere on how positioning, anchor points, and origin works for Sprite nodes, especially those that are children of other nodes.

Here’s a quick update to HelloWorld to give make seeing what is going on a little easier. First made follow/move action durations much longer. Simplified positions (even though I’m guessing you were going for center of screen vertically). Clarify a few things, etc.

This doesn’t solve your issue yet – when I get a chance to spend a little more time I will test out Node/World/Screen coordinates and see why they won’t work or how to use them successfully the way you’ve set up your test.


init:

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
	// 1. super init first
	if (!Scene::init())
	{
		return false;
	}

    this->_mouseListener_3->onMouseDown=CC_CALLBACK_1(HelloWorld::onMouseDown,this);
    _eventDispatcher_3->addEventListenerWithSceneGraphPriority(_mouseListener_3,this);

    auto s = Director::getInstance()->getWinSize();

    // Background
    auto bg = Sprite::create("q.png");
    bg->setPosition(0,0);
    bg->setPosition(1000,50);
    this->addChild(bg,0);

    // "Player" or something?
	this->node2=Sprite::create("ty.png");
	node2->setPosition(Vec2(100,50));
	this->addChild(node2,4);

    // Run Test Actions
    auto pFollow = Follow::create(node2,Rect(0, 0, s.width+480, s.height));
	this->runAction(pFollow);

    // slowed this one down to more easily see what's going on
	auto moveTo = MoveTo::create(80, Vec2(1000,50));
	node2->runAction(moveTo);

	return true;
}

mouseDown:

void HelloWorld::onMouseDown(Event* event){
    auto eventMouse = (EventMouse *)event;
    Vec2 mouseLocation = Vec2(eventMouse->getCursorX(), eventMouse->getCursorY());

//    // Here's a way to get the same node without an class data member (ie: this->node2)
//    Node* node = this->getChildByTag(1234);
//    if (!node) {
//        node = Sprite::create("ty.png");
//        node->setTag(1234);
//        this->addChild(node,3);
//    }
//    node->setPosition(mouseLocation);

    auto nodeMouse1 = Sprite::create("ty.png");
    nodeMouse1->setPosition(mouseLocation);
    nodeMouse1->setColor(cocos2d::Color3B::GREEN);
    this->addChild(nodeMouse1, 5);

    auto nodeMouse2 = Sprite::create("ty.png");
    nodeMouse2->setPosition(mouseLocation);
    nodeMouse2->setColor(cocos2d::Color3B::MAGENTA);
    this->node2->addChild(nodeMouse2, 5);

    CCLOG("node pos: {%.2f, %.2f}", mouseLocation.x, mouseLocation.y);

//    Vec2 worldLocation = this->convertToWorldSpace(mouseLocation);
//    node->setPosition(worldLocation);
}

Alright, in case you still haven’t figured it out, here’s a debug version that outputs position info and then shows placing a new sprite both on the root scene (which scrolls left along with the background) as well as placing it as attached to the this->node2 sprite (which then looks as if it stays in place at the mouse location).

(All these are in HelloWorldScene.cpp - I don’t believe I changed anything in the .h header file)


// on "init" you need to initialize your instance
bool HelloWorld::init()
{
	// 1. super init first
	if (!Scene::init())
	{
		return false;
	}

    this->_mouseListener_3->onMouseDown=CC_CALLBACK_1(HelloWorld::onMouseDown,this);
    _eventDispatcher_3->addEventListenerWithSceneGraphPriority(_mouseListener_3,this);

    auto s = Director::getInstance()->getWinSize();

    // Background
    auto bg = Sprite::create("q.png");
    bg->setPosition(0,0);
    bg->setPosition(1000,50);
    this->addChild(bg,0);

    // "Player" or something?
	this->node2=Sprite::create("ty.png");
	node2->setPosition(Vec2(100,50));
	this->addChild(node2,4);

    // Run Test Actions
    auto pFollow = Follow::create(node2,Rect(0, 0, s.width+480, s.height));
	this->runAction(pFollow);

    // slowed this one down to more easily see what's going on
	auto moveTo = MoveTo::create(80, Vec2(1000,50));
	node2->runAction(moveTo);

	return true;
}
// ----------------------------------------------------------------------

// A quick and dirty log to avoid repetative typing for logging

#define CCLOG_VEC2(varname) CCLOG_Vec2(#varname, varname)
static void CCLOG_Vec2(std::string name, Vec2 v) {
    CCLOG("%s: {%.2f, %.2f}", name.c_str(), v.x, v.y);
}

// ----------------------------------------------------------------------
void HelloWorld::onMouseDown(Event* event){
    auto eventMouse = (EventMouse *)event;

    // The mouse location in Screen Coordinates
    const Vec2 mouseLocation = Vec2(eventMouse->getCursorX(), eventMouse->getCursorY());
    CCLOG_VEC2(mouseLocation);

    // ----------------------------------------------------------------------

    // Here we attach to the scene itself (ie: the root Node), so
    // we need to convert the mouseLocation to the "world" position
    // of the scene (or root node).

    Vec2 worldOriginOffset = this->getPosition();
    CCLOG_VEC2(worldOriginOffset);

    // This is essentially a "screen to world" conversion
    // ("world" is usually root node of the game layer/scene)
    Vec2 mouseLocationInWorldCoords = mouseLocation - worldOriginOffset;
    CCLOG_VEC2(mouseLocationInWorldCoords);

    auto nodeMouse1 = Sprite::create("ty.png");
    nodeMouse1->setPosition(mouseLocationInWorldCoords);
    nodeMouse1->setColor(cocos2d::Color3B::GREEN);
    this->addChild(nodeMouse1, 5);

    // ----------------------------------------------------------------------

    // Here we attach to `node2`, which would keep it "attached" to node2 if node2 moves

    // Need to convert the location

    Vec2 node2pos = this->node2->getPosition();
    CCLOG_VEC2(node2pos);

    // This is a test only, only affects logging the coord,
    // not used for anything else in this example.
    Vec2 node2PosInWorldCoords = this->node2->convertToWorldSpace(node2pos);
    CCLOG_VEC2(node2PosInWorldCoords);

    Vec2 screenOriginInWorldCoords = Vec2(0,0) - worldOriginOffset;
    Vec2 node2PosInScreenCoords = node2pos - screenOriginInWorldCoords;
    CCLOG_VEC2(node2PosInScreenCoords);

    Vec2 node2anchor = this->node2->getAnchorPoint();
    CCLOG_VEC2(node2anchor);

    // When we set the position on a new node it's positioned off the lower-left corner of the ..
    // .. Node/Sprite's "content" rect (the default rect is defined by the size of the Sprite's texture)
    Vec2 anchor = this->node2->getAnchorPoint();
    Size s = this->node2->getContentSize();
    Vec2 node2CenterOffset = Vec2(anchor.x * s.width, anchor.y * s.height);
    CCLOG_VEC2(node2CenterOffset);

    // This is essentially a "screen to node" conversion
    Vec2 mouseLocationInNode2Coords = mouseLocation - node2PosInScreenCoords + node2CenterOffset;
    CCLOG_VEC2(mouseLocationInNode2Coords);

    // Create and position a new sprite at the mouse location, but attached to `this->node2`
    auto nodeMouse2 = Sprite::create("ty.png");
    nodeMouse2->setPosition(mouseLocationInNode2Coords);
    nodeMouse2->setColor(cocos2d::Color3B::MAGENTA);
    this->node2->addChild(nodeMouse2, 5);

    // ----------------------------------------------------------------------

    //    // Here's a way to get the same node without an class data member (ie: this->node2)
    //    Node* node = this->getChildByTag(1234);
    //    if (!node) {
    //        node = Sprite::create("ty.png");
    //        node->setTag(1234);
    //        this->addChild(node,3);
    //    }
    //    node->setPosition(mouseLocationInWorldCoords);

    // ----------------------------------------------------------------------

    CCLOG("----------------------------------------");
}

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.