Accessing variable in lambda crashes game

Global variables to lambda???.. You can only capture function-level variables to lambdas. Retaining here is irrelevant here as well. Most likely only mapBackground is derived from Ref and that is internally retained by the addChild method.

Im talking globally relative to the lambda i.e member variables variables, or variables that are not allocated on the stack.

@MatrixAndrew you did couple of errors in your code.
At first compile error - missing ; in lambda body.
Second - initial value of alive should be true.

Full working code:

#include <memory>
#include <string>

auto alive = std::make_shared<bool>(true);
auto lambda = [alive](){ *alive = !*alive; };
lambda();
cocos2d::log((std::string("First, it was alive, now it is ") + (*alive ? "still alive" : "dead")).c_str());

Yeah, you’re right, thanks.

@amin13a That wouldn’t work as player is a child of mapBackground and accessing mapBackground also crashes.

@MatrixAndrew The variables “ammo” and “alive” are working fine, only “player” and “mapBackground” are causing the game to crash.

@sheshmoshen My variables are created globally. They are under the public part of my class. Could you elaborate a bit more on retaining all Ref derived objects as I’m pretty new to C++.

make sure these variables declared in .h : playerNumber, velocity, BULLETSPEED, playerNumber ? maybe you missed one.

bullet = Sprite::create(“bullet”+to_string(playerNumber)+".png");
double check Resources folder, this could be a simple error.

The game crashes after “third” : could be file not found, and “eight” : add child bullet, but bullet is null.

C++ as a language forces the programmer to manage memory.
Therefore when you create a cocos2d-x object that is allocated by calling
the new keyword that will go on the heap which is basically objects that are in a
certain region in memory that obligates me to manage it.

Any Ref derived object (Node, Sprite, Action and many others) need to be retained so that the local cocos2d-x memory manager won’t delete that object rather it will retain it until I release it.

If I add my Ref derived object to another Ref retained object than I don’t have to call retain on my own.

But if I don’t want my Ref derived object to be added in init() I need to specifically retain it to tell cocos2d-x “HEY DON’T DELETE MY OBJECT BECAUSE I WILL USE IT LATER!”

I tried retaining bullet, mapBackground, and player but it still crashes in the exact same spot.

Please post here full class header and source.

1 Like

Could be your initialization is null.
Maybe you are loading the wrong file?

But like @dimon4eg said post your source code that pertains to this region of your game.

#include <string>
#include "cocos2d.h"
#include "ui/CocosGUI.h"

#define DEGREES (180/3.14159265359)
#define BULLETSPEED 200
#define PLAYERSPEED 150

using namespace cocos2d;
using namespace cocos2d::ui;
using namespace std;

class Player {
		Sprite* stick;
		int lives;
		float angle = 0;
		Vec2 startPosition;
		Vec2 startVelocity;
	
	public:
		bool alive = true;
		int touch = 999;
		Sprite* joy;
		Sprite* bullet;
		Sprite* player;
		TMXTiledMap* mapBackground;
		int playerNumber;
		int bulletNumber;
		Vec2 velocity;
		int ammo = 1;

		Player();
	
		Player(int a, Vec2 b, TMXTiledMap* c, int d, Vec2 e, Vec2 f, Vec2 g, Vec2 h, Vec2 i) {
			playerNumber = a;
			bulletNumber = a*11;
			startPosition = b;
			mapBackground = c;
			lives = d;
			startVelocity = e;
			velocity = e;
		
			player = Sprite::create("player"+to_string(playerNumber)+".png");
			player->setPosition(b);
			auto playerPhysics = PhysicsBody::createBox(Size(20, 20), PhysicsMaterial(0, 0, 0));
			player->addComponent(playerPhysics);
			player->getPhysicsBody()->setCollisionBitmask(playerNumber);
			player->getPhysicsBody()->setContactTestBitmask(true);
			mapBackground->addChild(player);
		
			joy = Sprite::create("joy"+to_string(playerNumber)+".png");
			joy->setAnchorPoint(f);
			joy->setPosition(g);
			mapBackground->getParent()->addChild(joy);
		
			stick = Sprite::create("stick.png");
			stick->setPosition(joy->getContentSize()/2);
			joy->addChild(stick);
		
			auto shoot = Button::create("shoot"+to_string(playerNumber)+".png");
			shoot->setAnchorPoint(h);
			shoot->setPosition(i);
			shoot->addTouchEventListener([&](Ref* sender, Widget::TouchEventType type){
				switch (type) {
					case Widget::TouchEventType::ENDED:
						if (ammo && alive) {
							CCLOG("first");
							auto bulletPhysics = PhysicsBody::createCircle(5, PhysicsMaterial(0, 1, 0));
							CCLOG("second");
							bullet = Sprite::create("bullet"+to_string(playerNumber)+".png");
							CCLOG("third");
							bullet->setPosition(player->getPosition()+velocity*20); //crashes here when accessing player
							CCLOG("fourth");
							bullet->setPhysicsBody(bulletPhysics);
							CCLOG("fifth");
							bullet->getPhysicsBody()->setVelocity(velocity*BULLETSPEED);
							CCLOG("sixth");
							bullet->getPhysicsBody()->setCollisionBitmask(bulletNumber);
							CCLOG("seventh");
							bullet->getPhysicsBody()->setContactTestBitmask(true);
							CCLOG("eighth");
							mapBackground->addChild(bullet); //crashes here when accessing mapBackground
							CCLOG("ninth");
							ammo = 0;
						}
						break;
					default:
						break;
				}
			});
			mapBackground->getParent()->addChild(shoot);
		};

Player instance may be destroyed somewhere. You have to own it (inherit from cocos2d::Ref and retain it or use shared_ptr/unique_ptr).

I don’t see problem in Player class.
But better not to use & in lambda, use ‘this’ instead:

shoot->addTouchEventListener([this](Ref* sender, Widget::TouchEventType type){

Please post code how you create and hold player object.
Maybe problem there.

If Player is destroyed then accessing player (which is a member of Player) will cause a segment fault.
If you use Xcode, turning on address sanitizer will help a lot.

1 Like

Sorry for the extremely late reply, I’ve been extremely busy these last few weeks.

I create my player objects in my .cpp file add them into a vector.

Player one (playersNumber, Vec2(50, 50), mapBackground, lives, Vec2(0, 1), Vec2(1, 0), Vec2(visibleSize.width*2/9, visibleSize.width/9), Vec2(0, 0), Vec2(visibleSize.width*2/9, visibleSize.width/20));
players.push_back(one);

How would I make sure it’s retained?
Also, is there something like address sanitizer for Windows?

Maybe you can set a breakpoint on Player’s destructor. If it breaks there, it has been deleted (thus not retained). If this is the case and you don’t want to delete it, call:

player->retain();

After more testing, I realised it wasn’t the player variable that was causing the crash but rather the bullet variable.

Once I retain the bullet variable, the game no longer crashes but instead, a new problem occurs.

I have four different players but no matter which player’s shoot button I press, it is always player 4 who shoots. I’m pretty sure this has something to do with the fact that it is the last player initialised but I have no idea why.

Any help would be appreciated.

EDIT:
I think what I need is something like this but using C++

Using push_back will copy the object and the original Player will still be destroyed.

Solution:
Use std::unique_ptr:

std::vector<std::unique_ptr<Player>> players;
...
auto one = std::unique_ptr<Player>(new Player(playersNumber, Vec2(50, 50), mapBackground, lives, Vec2(0, 1), Vec2(1, 0), Vec2(visibleSize.width*2/9, visibleSize.width/9), Vec2(0, 0), Vec2(visibleSize.width*2/9, visibleSize.width/20)));
players.push_back(std::move(one));

Or emplace_back:

players.emplace_back(playersNumber, Vec2(50, 50), mapBackground, lives, Vec2(0, 1), Vec2(1, 0), Vec2(visibleSize.width*2/9, visibleSize.width/9), Vec2(0, 0), Vec2(visibleSize.width*2/9, visibleSize.width/20));

I tried using std::unique+ptr but now I am no longer able to access the variables under it when looping. For example, I have the following code:

for (auto &j : players) {
			if (j.alive) { //some more stuff

and it gives the error error: 'class std::__ndk1::unique_ptr<Player>' has no member named 'alive'.

EDIT: I solved by replacing j with (*j) like what this link said

It also turns out that this was the whole problem all along. I now no longer need to physically retain any variables anymore. Thank you to everyone who helped me solve this problem