Accessing variable in lambda crashes game

Accessing variable in lambda crashes game
0.0 0

#1

Here is my current code

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);
				CCLOG("fourth");
				bullet->setPhysicsBody(bulletPhysics);
				CCLOG("fifth");
				bullet->getPhysicsBody()->setVelocity(velocity*BULLETSPEED);
				CCLOG("sixth");
				bullet->getPhysicsBody()->setCollisionBitmask(playerNumber*11);
				CCLOG("seventh");
				bullet->getPhysicsBody()->setContactTestBitmask(true);
				CCLOG("eighth");
				mapBackground->addChild(bullet);
				CCLOG("ninth");
				ammo = 0;
			}
			break;
		default:
			break;
	}
});
mapBackground->getParent()->addChild(shoot);

The game crashes after “third” is logged to the console and after a bit of tinkering, I found out that accessing the variable “player” is what crashes the game. Also, if I commented out that line the game would also crash after “eight” when accessing “mapBackground”.

Both variable “player” and “mapBackground” are declared in the public part of my header file so I have no idea why this is crashing. Also, accessing the other variables does not cause the game to crash.

Any help would be greatly appreciated.


#2

You need to copy the values not capture by reference


#3

Make sure player & mapBackground is not NULL,


#4

Change [&] to [this].


#5

@sheshmoshen I need to capture by reference so copying is not an option.
@sevent7 I tested player and mapBackground and they are not NULL.
@amin13a I tried changing [&] to [this] but it still crashed in the same place as before.


#6

try this:
when you create player : player->setName(“player”);
and change in lambda: getChildByName(“player”)->getPosition() + …


#7

Do you suppose we will solve your memory read access violation errors without you even linking the code to the variables (ammo, alive, playerNumber, mapBackground) that are captured by reference in the lambda?
A hint: forget capture by reference, especially capturing all variables by reference ( [&] ). If you need similar behavior use a variable on a more global scope, or maybe something like this:

#include <memory>
#include <string>

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

It should output it’s dead; the alive boolean is preserved at least until the lambda is not destroyed.


#8

Ok just asking are you sure you created your variables globally?
if not than capturing by reference simply is not allowed.

If you are storing everything globally than make sure you retain all Ref derived objects.


#9

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.


#10

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


#11

@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());

#12

Yeah, you’re right, thanks.


#13

@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++.


#14

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.


#15

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!”


#16

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


#17

Please post here full class header and source.


#18

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.


#19
#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);
		};

#20

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