Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
How can i destroy all my scene objects on scene exit? — Gideros Forum

How can i destroy all my scene objects on scene exit?

AxlFlameAxlFlame Member
edited August 2012 in Game & application design
Hi,
I'm having this issue and i'm getting desperate about it...

I have a game where i create many blocks and make them go from right to left via parallax effect and between them there's space for the player to draw a bridge so the character would not fall. When they exit the screen i destroy the blocks and create the new one on the right side.
I also have a pause screen from where i can exit the game and return to the title screen. My problem is that whenever i exit the scene, the physical bodies of the blocks somehow stays on the stage, even though i'm not at the game scene anymore, that is causing the game to crash if i try to draw something over the body that is already there.

How can i destroy those remaining bodies? I managed to destroy the player's character one, but i can't seem to be able to reference the bodies from the bridge and the blocks....

I tried to upload my .lua files but the forum refuses to let me lol So i'm pasting them here...

Please, any help would be deeply appreciated.




Comments

  • AxlFlameAxlFlame Member
    edited August 2012
    Ok, so my code is too big and i can't post it either... so here goes a few parts...

    This is the function that creates the blocks
    newBlock = function()
    	--this function includes all the "paths" logic.
    	local block;
    	--if the currentBlock needed is the first,
    	--then we use a different image (headBlockIMG), decide the length of the new path,
    	--decide it's distance from the previous one, and add a physics body that is as big as the whole path.
    	if currentBlock == 1 then
    		currentPathLen = mRandom(minPathLen, maxPathLen);
    		dropEnemyAtBlock = mRandom(2, currentPathLen-1);
    		block = newBitmap(BlocoIMG);
    		block:setAnchorPoint(0.5, 0.5);
    		sceneParent:addChild(block);
    		if lastBlock then
    			block:setPosition(lastBlock:getX()+mRandom(minDistWidth, maxDistWidth),
    							  mMin(minHeight,mMax(maxHeight, lastBlock:getY()+mRandom(-minDistHeight, maxDistHeight))));
    		else
    			--let's set first block of the game position
    			block:setPosition(block:getWidth()*0.5, 220);
    		end
    		block.body = b2World:createBody{type = b2STATIC_BODY, 
    						position = {x = block:getX(), y = block:getY()},
    					   };
    		block.body.name = "block";
    		block.body.sprite = block;
    		local blockShape = b2PolygonShape.new();
    		local blockWidth, blockHeight = block:getWidth(), block:getHeight();
    		blockShape:set(-blockWidth*0.5, -blockHeight*0.5,
    						-blockWidth*0.5+blockWidth*currentPathLen, -blockHeight*0.5,
    						-blockWidth*0.5+blockWidth*currentPathLen, blockHeight*0.5,
    						-blockWidth*0.5, blockHeight*0.5);
    		block.body:createFixture{shape = blockShape,
    							friction = 0,
    							restitution = 0,
    							density = 10,
    							isSensor = false
    						   };
    		block.removeAt = block:getWidth()*currentPathLen;
    		function block:onEnterFrame()
    			self:setX(self:getX()-layer1Speed);
    			self.body:setPosition(self:getPosition());
    			if not self.created then
    				if self:getX() < screenWidth then
    					self.created = true;
    					newBlock();
    				end
    			else
    				if self:getX() < -block.removeAt then
    					b2World:destroyBody(self.body);
    					self.body = nil;
    					self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame, self);
    					sceneParent:removeChild(self);
    				end
    			end
    		end
    	elseif currentBlock == currentPathLen then
    		--if the block is the last of the path, then we use a "endBlockIMG".
    		currentBlock = 0;
    		block = newBitmap(BlocoIMG);
    		block:setAnchorPoint(0.5, 0.5);
    		sceneParent:addChild(block);
    		block:setPosition(lastBlock:getX()+block:getWidth(), lastBlock:getY());
    		function block:onEnterFrame()
    			self:setX(self:getX()-layer1Speed);
    			if not self.created then
    				if self:getX() < screenWidth then
    					self.created = true;
    					newBlock();
    				end
    			else
    				if self:getX() < -50 then
    					self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame, self);
    					sceneParent:removeChild(self);
    				end
    			end
    		end
    	else
    		block = newBitmap(BlocoIMG);
    		block:setAnchorPoint(0.5, 0.5);
    		sceneParent:addChild(block);
    		block:setPosition(lastBlock:getX()+block:getWidth(), lastBlock:getY());
    		function block:onEnterFrame()
    			self:setX(self:getX()-layer1Speed);
    			if not self.created then
    				if self:getX() < screenWidth then
    					self.created = true;
    					newBlock();
    				end
    			else
    				if self:getX() < -50 then
    					self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame, self);
    					sceneParent:removeChild(self);
    				end
    			end
    		end
    		--we only want monsters to drop on the "middle" blocks, so here we check if we must create one, and decide randomly which one.
    		if currentBlock == dropEnemyAtBlock then
    				dropEnemyAtBlock = 0;
    				local enemyType = mRandom(1, #enemyTable);
    				local enemy = enemyTable[enemyType](enemyTexture[enemyType], block:getX(), block:getY()-(block:getHeight()*0.5), b2World);
    				sceneParent:addChild(enemy);
    				enemy.moveMe = function(object)
    				local selfX, selfY = object:getPosition();
    				object:setPosition(selfX-layer1Speed, selfY);
    				return b2World;
    			end
    			enemy:addListeners();
    		end
    	end
    	function block:removeListeners()		
    		block:removeEventListener(Event.ENTER_FRAME, block.onEnterFrame, block);
    	end
    	function block:addListeners()
    		block:addEventListener(Event.ENTER_FRAME, block.onEnterFrame, block);
    	end
    	currentBlock = currentBlock+1;
    	block:addEventListener(Event.ENTER_FRAME, block.onEnterFrame, block);
    	lastBlock = block;
    end
  • This is the init from my game scene, it includes the function i use to allow the gestures recognition with the help of the Gestures.lua file.
    --on scene initialization
    function bonecosDesenho:init()
     
    	if sets.music then
    		music.channel = music.minigamesDesenho:play(0,1000000)
    	end	
     
    	-- Resetando alguns valores
    	lastBlock = nil;
    	currentBlock = 1;
    	currentScore = 0;
    	--currentLifes = 3;
     
    	enemyTable = {bDobstaculos.new,
    				bDobstaculos.new};	
     
    	--create world instance
    	b2World = b2.World.new(0, 18, true)
    	b2PolygonShape = b2.PolygonShape;
    	b2STATIC_BODY = b2.STATIC_BODY;		
     
    	_G.b2World = b2World;
    	sceneParent = self;
    	createParallaxBG();
    	newBlock();	
    	currentPathLen = 20;
     
    	--Criando e guardando referência pro boneco
    	self.boneco = self:boneco(100, 100)
     
    	--Recreating first block body due to the manual change in pathLen
    	b2World:destroyBody(lastBlock.body);	
    	lastBlock.body = b2World:createBody{type = b2STATIC_BODY, 
    										position = {x = lastBlock:getX(), y = lastBlock:getY()},
    									   };
    		local blockShape = b2PolygonShape.new();
    		blockShape:set(-lastBlock:getWidth()*0.5, -lastBlock:getHeight()*0.5,
    						-lastBlock:getWidth()*0.5+lastBlock:getWidth()*currentPathLen, -lastBlock:getHeight()*0.5,
    						-lastBlock:getWidth()*0.5+lastBlock:getWidth()*currentPathLen, lastBlock:getHeight()*0.5,
    						-lastBlock:getWidth()*0.5, lastBlock:getHeight()*0.5);
    		lastBlock.body:createFixture{shape = blockShape,
    							friction = 0,
    							restitution = 0,
    							density = 10,
    							isSensor = false
    						   };
    	lastBlock.removeAt = lastBlock:getWidth()*currentPathLen;
    	lastBlock.body.name = "block";
    	lastBlock.body.sprite = lastBlock;
    	dropEnemyAtBlock = 0;	
     
    	--set up debug drawing
    	--[[local debugDraw = b2.DebugDraw.new()
    	self.world:setDebugDraw(debugDraw)
    	self:addChild(debugDraw)--]]
     
    	--Adding the score
    	objScore = GCscore.new({Texture.new("images/0.png"), Texture.new("images/1.png"), Texture.new("images/2.png"),
    							Texture.new("images/3.png"), Texture.new("images/4.png"), Texture.new("images/5.png"),
    							Texture.new("images/6.png"), Texture.new("images/7.png"), Texture.new("images/8.png"), 
    							Texture.new("images/9.png")},
    							25);
    	self:addChild(objScore);
    	objScore:setX(screenWidth-30);
    	objScore:setScore(100);
    	objScore:setScore(20);
     
    	--adding the pause button
    	local pauseButton = GCpauseBut.new(newTexture("Buttons/PauseSmall.png"));
    	self:addChild(pauseButton);
    	pauseButton:setPosition(screenWidth*0.01, 4);	
     
    --===================================================
    --				     GESTOS
    --===================================================
     
    --Shape para servir de scopo do verificador de gestos
    local areaGestos = Shape.new()
    		areaGestos:setFillStyle(Shape.SOLID, 0xC0C0C0, 0.5)   
    		areaGestos:beginPath(Shape.NON_ZERO)
    		areaGestos:moveTo(0, 195)
    		areaGestos:lineTo(application:getContentWidth(), 195)
    		areaGestos:lineTo(application:getContentWidth(), 225)
    		areaGestos:lineTo(0, 225)
    		areaGestos:lineTo(0, 195)
    		areaGestos:endPath()
    		self:addChild(areaGestos)
     
    local font = TTFont.new("Fontes/tahoma.ttf", 20)
     
    local myText = TextField.new(font, "Result: ")
    myText:setPosition(50,50)
    self:addChild(myText)
     
    self.gest = Gestures.new({
    	debug = true,
    	draw = true,
    	drawColor = 0xff0000,
    	drawWidth = 5,
    	autoTrack = true,
    	scope = areaGestos,
    	allowRotation = true,
    	inverseShape = true,
    	points = 33
    })
     
    self.callback = function(name)
    	local debugDraw = b2.DebugDraw.new()
    	b2World:setDebugDraw(debugDraw)
    	stage:addChild(debugDraw)
    	myText:setText(name)
    	--Update
    	local xIni = self.gest.points[1].x;
    	local xFim = self.gest.points[#self.gest.points].x;
    	local yIni, yFim = 196, 225;
    	--Apaga o ultimo shape para recriar
    	bridge:clear()
    	if bridge.body then
    		b2World:destroyBody(bridge.body)
    	end
    	--Criando novo shape
    	bridge:setFillStyle(Shape.SOLID, 00000000, 0.5) 
    	bridge:beginPath(Shape.NON_ZERO)
    	--calculate drawn width
    	local width = self.gest.points[#self.gest.points].x - self.gest.points[1].x
    	print(self.gest.points[#self.gest.points].x, self.gest.points[1].x)
    	--drawing shape from 0 coordinates
    	bridge:moveTo(0, yIni)
    	bridge:lineTo(width, yIni)
    	bridge:lineTo(width, yFim)
    	bridge:lineTo(0, yFim)
    	bridge:lineTo(0, yIni)
    	bridge:endPath()
    	areaGestos:addChild(bridge)
    	--adding to expected position
    	bridge:setX(self.gest.points[1].x)
    	--if drawing from left to right
    	if self.gest.points[1].x < self.gest.points[#self.gest.points].x then
    		bridge.left2right = true
    	else
    		bridge.left2right = false
    	end
     
    	bridge:setY(yFim/yIni)
     
    	--Criando corpo fisico p/ ponte
    	bridge.body = b2World:createBody{type = b2STATIC_BODY, 
    									position = {x = xFim/xIni, y = yFim-4},
    									};
    	bridge.body.name = "bridge";	
    	local bridgeShape = b2.PolygonShape.new()
    	local bridgeWidth = bridge:getWidth()
    	local bridgeHeight = bridge:getHeight()
    	bridgeShape:set(-bridgeWidth, -bridgeHeight,
    					-bridgeWidth+bridgeWidth, -bridgeHeight,
    					-bridgeWidth+bridgeWidth, bridgeHeight,
    					-bridgeWidth, bridgeHeight);	
    	bridge.body:createFixture{shape = bridgeShape,
    						friction = 0,
    						restitution = 0,
    						density = 10,
    						isSensor = false
    						};	
    	local teste = bridge:getX()
    	print (teste)
    	--you don't need to define same methods all the time
    	--and certainly don't need to add onEnterFrame event on each callback
    	--there will be multiple on Enter frame events
    	if not bridge.onEnterFrame then
    		--movendo o shape e o corpo
    		function bridge:onEnterFrame()
    			--calculate new bridge x
    			local bridgeX = bridge:getX()-3
    			--change bridge position
    			bridge:setX(bridgeX)
    			if bridge.left2right then
    				bridgeX = bridgeX + bridge:getWidth()
    			end
    			--if bridge x has body
    			if bridge.body then
    				--set bodies position alongside with bridge
    				local _, bodyY = bridge.body:getPosition()
    				bridge.body:setPosition(bridgeX, bodyY)
    			end
    			if bridge:getX() < -480 then
    				bridge:clear()
    				bridge:removeListeners()
    			end
    		end
    		function bridge:removeListeners()
    			bridge:removeEventListener(Event.ENTER_FRAME, bridge.onEnterFrame, bridge);
    		end
    		function bridge:addListeners()
    		bridge:addEventListener(Event.ENTER_FRAME, bridge.onEnterFrame, bridge);
    		end	
    		bridge:addEventListener(Event.ENTER_FRAME, bridge.onEnterFrame, bridge);
    	end
    end
     
    self.gest:addGesture("Line", {
    	{x = 0, y = 0},
    	{x = 0, y = 100}
    }, self.callback)
     
    self.gest:addGesture("ZigZag", {
    	{x = 0, y = 0},
    	{x = 50, y = 87},
    	{x = 100, y = 0},
    	{x = 150, y = 87},
    }, callback)
     
    local x = 0
    local y = -100
    local circle = {}
    local totalPoints = 72
    local step = (math.pi*2)/totalPoints
     
    for angle = 1, totalPoints do
    	local newX = x*math.cos(angle*step)-y*math.sin(angle*step)
    	local newY = y*math.cos(angle*step)+x*math.sin(angle*step)
    	local point = {x = newX, y = newY}
    	table.insert(circle, point)
    end
     
    self.gest:addGesture("Circle", circle, callback)
     
    --===================================================
    --				 FIM GESTOS
    --===================================================
    	--run world
    	self:addListeners();
    	self:addEventListener(Event.REMOVED_FROM_STAGE, self.removedFromStage, self);	
    	self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    	--remove event on exiting scene
    	self:addEventListener("exitBegin", self.onExitBegin, self)
    end
  • And finally this is the function i was able to use to destroy the character's body.
    --removing event on exiting scene
    function bonecosDesenho:onExitBegin()
    	print("oi")
    	b2World:destroyBody(self.boneco.body);
    	sceneParent = nil;
    	self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    end
  • john26john26 Maintainer
    Can you show the code where you destroy the blocks and bridges? Your function bonecosDesenho:onExitBegin() looks fine. You need to do the same thing for the other objects...
  • So, i wasn't actually destroying the bridge body, but I added this to the onExitBegin and it worked.
    function BonecosDesenho:onExitBegin()
    	b2World:destroyBody(self.boneco.body);
    	b2World:destroyBody(bridge.body);
    	sceneParent = nil;
    	self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    end
    If I try to follow that line and use this:
    b2World:destroyBody(block.body);
    It gives me this error:
    attempt to index global 'block' (a nil value)

    Sorry for taking so long... any help would be deeply appreciated.
  • It gives me this error:
    attempt to index global 'block' (a nil value)

    generally this error will come when you are trying to access object which is already removed or that is a local inside any function[and hence not accessible where you are trying to access] if you are sure that it is not removed then you might have declared it as local in any other function try to remove that local and make that global after this every thing should work fine

    also in the case you want to delete everything onExitBegin use this
    	for i=self:getNumChildren(),1,-1 do
    		local sprite = self:getChildAt(i)	
    		if sprite.body then
     
    			world:destroyBody(sprite.body)
    		end
    sprite:removeFromParent()--usually it will not needed as scenemanager do this for you
    	end
    :)

    Likes: AxlFlame

    +1 -1 (+1 / -0 )Share on Facebook
  • Ok, i'm sorry for the dumb question, but to make the variable global, I simply need to remove the 'local' before? Indeed the block is being created as local inside the NewBlock function.
    Assuming its that, I got another error:

    "attempt to perform arithmetic on field 'removeAt' (a nil value)"

    removeAt is a property of block, and after I tried to change block to global aparrently it started to receive nil...
    That block.removeAt is located inside the NewBlock function I pasted here, halfway, right before the block onEnterFrame.

    Btw, thanks for the advice regarding destroying everything! It works perfectly... except for the block of course =/
  • So, I tried switching back block to local block as I noticed that the problem is not that the blocks aren't being destroyed, its the bridge that isn't... at least using your code for destroying everything on the scene.

    That said I tried to use this to destroy the bridge:
    b2World:destroyBody(bridge.body);
    But when I try that, and go to start this game scene again it says:

    Body is already destroyed.

    And the error points to this part:
    if bridge.body then
    	b2World:destroyBody(bridge.body)
    end
    What I don't understand is why do I get this error, if the 'if' is exactly to avoid that?
Sign In or Register to comment.