Question

I'm new to Lua. I'm trying to create a game using Cocos2d-x v3.1rc0.

I'm running into an issue where it appears that one of the objects, created by the Cocos2d-x lib, is being garbage collected before it should be.

Here is a class to keep track of "prey" on the screen. Each prey references frames/animations that will be displayed depending on the state of the prey.

Prey = {
    sprite = false -- Main prey sprite.
    -- Frames used for movement, getting hit, knocked out, etc.
  , frame = {
        idle = false -- idle frames
      , move = false -- move frames
      , rest = false -- resting frames
    }
  , state = false -- The current state of the Prey.
}

Here is the constructor:

function Prey:new()
    local o = {}
    setmetatable(o, self)
    self.__index = self
    return o
end

Here is where the frames are associated to the prey:

function Prey:setFrames(frames)
    --[[ Moving ]]--
    self.frame.move = cc.Animation:createWithSpriteFrames({frames[1], frames[2], frames[3]}, 1.0)
    cclog("move frames: " .. #self.frame.move:getFrames())

    --[[ Resting ]]--
    self.frame.rest = cc.Animation:createWithSpriteFrames({frames[4], frames[5]}, 2.0)
    cclog("rest frames: " .. #self.frame.rest:getFrames())
end

The above will print the following:

cocos2d: [LUA-print] move frames: 3
cocos2d: [LUA-print] rest frames: 2

However, when I attempt to call the following method, the frame.move and frame.rest variables appear to be garbage collected because an error is raised when I attempt to access them. Please note that this method is called every tick:

function Prey:tick()
    cclog("state: " .. self.state)

    local animation = false

    -- Moving
    if (self.state == PreyState.MOVING)
    then
        cclog("moving: " .. #self.frame.move:getFrames())
        animation = self.frame.move
    elseif (self.state == PreyState.RESTING)
    then
        cclog("resting: " .. #self.frame.rest:getFrames())
        -- Resting
        animation = self.frame.rest
    end
end

When the cclog calls are being made for either of the two conditions the following error is displayed. Please note that I know that this specific instance of Prey has not been garbage collected because self.state was set to idle before I made the call to the tick method. It also retains self.state on subsequent calls to this method.

cocos2d: [LUA-print] state: 2
cocos2d: [LUA-print] ----------------------------------------
cocos2d: [LUA-print] LUA ERROR: [string "Prey.lua"]:189: invalid 'cobj' in function  'lua_cocos2dx_Animation_getFrames'

After looking at many articles describing how objects are retained, it appears that it's possible that my Animation object is being garbage collected. But I have no idea why! The reference to the Cocos2d-x object should be strong, right?

Here are some articles I've read regarding the subject:

UPDATE 1

The following code is what causes the issue:

-- Create the instance of our prey object here...
prey = new Prey:new()
local function tick()
    prey:tick()
end
scheduleID = cc.Director:getInstance():getScheduler():scheduleScriptFunc(tick, 0, false)

However, when I attempt to simply call prey:tick() outside of the scheduler I get NO errors. I need this code to be ran every tick... what am I missing? In this particular scenario I have made the prey a global variable so that the tick method could access the only instance of Prey. I did this to simplify this particular test. However, I'd like to make prey local and make the scheduler run the tick function for every instance of Prey.

Était-ce utile?

La solution

The objects are being garbage collected. I have ran several more tests and came to the conclusion that you can not associate any Cocos2d objects to a variable without them later being garbage collected on the next cycle. Instead, you have to use Cocos2d-x's libs to cache your textures and then query for those textures at the time you need them.

It's difficult to illustrate, but what the end result was to call cc.Director:getInstance():getTextureCache():getTextureForKey("texture.png") at the time I needed the texture and re-create the frames needed for each animation when the state changed for the object. I'm going to look for a way to cache sprites so I don't have to re-create them. However, this resolved my issue. So the lesson is that Cococ2d objects get garbage collected on the next cycle. The only objects that are retained are those that are cached internally by Cocos2d. I could be wrong about this, but this is what I have observed.

So my process is to first add the image to the texture cache at load time:

local texture = cc.Director:getInstance():getTextureCache():addImage("texture.png")

And then calling the following later on:

local texture = cc.Director:getInstance():getTextureCache():getTextureForKey("texture.png")

I would really appreciate any insight into this. How are you guys handling this? Is this the right process? Are there any articles related to caching sprites, textures, etc for later use? Thank you!

UPDATE 1

Yes. The objects are being released from within the Cocos2d-x library. You can get around this by caching the sprite frames. This is the code I used, in Lua, to cache the sprite frames. After they are cached, any reference you hold to the SpriteFrame, from within your Lua code, will continue to point to an active instance of the frame.

cc.SpriteFrameCache:getInstance():addSpriteFrame(frame, name)

And to get the SpriteFrame back out of the cache:

local frame = cc.SpriteFrameCache:getInstance():getSpriteFrame(name)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top