How to get cocos2dx into a wxGLCanvas of wxWidgets - cocos2d-x

I'm trying to create a level editor for my futur game, to do that I need to get cocos2dx scene showing in a wxGLCanvas of the wxWidgets library.
How to link the wxGLCanvas with cocos2d::GLView ?
What I've done :
Created a class ccwxGLView that inherits from GLView:
class ccwxGLView : public cocos2d::GLView
{
ccwxGLCanvas* mGLCanvas;
public:
ccwxGLView(ccwxGLCanvas* canvas) {
setGLCanvas(canvas);
}
virtual ~ccwxGLView(void) {}
static ccwxGLView* create(ccwxGLCanvas* canvas) {
auto ret = new (std::nothrow) ccwxGLView(canvas);
if(ret) {
ret->autorelease();
return ret;
}
return nullptr;
}
void setGLCanvas(ccwxGLCanvas* canvas) {
mGLCanvas = canvas;
}
ccwxGLCanvas* getGLCanvas() {
return mGLCanvas;
}
HWND getWin32Window() {
return mGLCanvas->GetParent()->GetHWND();
}
/** Force destroying EGL view, subclass must implement this method. */
virtual void end() {
mGLCanvas->Close();
wxDELETE(mGLCanvas);
mGLCanvas = nullptr;
}
/** Get whether opengl render system is ready, subclass must implement this method. */
virtual bool isOpenGLReady() {
return (mGLCanvas && mGLCanvas->IsShown());
}
/** Exchanges the front and back buffers, subclass must implement this method. */
virtual void swapBuffers() {
mGLCanvas->SwapBuffers();
}
virtual void setIMEKeyboardState(bool open) {}
virtual bool windowShouldClose() {
return (mGLCanvas == nullptr);
}
};
Created a class that inherits from wxGLCanvas :
class ccwxGLCanvas : public wxGLCanvas
{
wxGLContext* m_context;
public:
ccwxGLCanvas(wxFrame* parent, int* args)
: wxGLCanvas(parent, wxID_ANY, args, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
{
m_context = new wxGLContext(this);
}
virtual ~ccwxGLCanvas() { delete m_context; }
void resized(wxSizeEvent& evt) {
Refresh();
}
void render(wxPaintEvent& evt) {
cocos2d::Director* director = cocos2d::Director::getInstance();
if(director != nullptr && director->getRunningScene() != nullptr)
director->drawScene();
}
};
In the AppDelegate I modified the glView initialization by :
if(!glview) {
glview = ccwxGLView::create(mGLCanvas);
director->setOpenGLView(glview);
}
And on the app init function, I wrote something like this :
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
frame = new wxFrame((wxFrame *)NULL, -1, wxT("Hello GL World"), wxPoint(50,50), wxSize(400,200));
int args[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0};
glPane = new ccwxGLCanvas( (wxFrame*) frame, args);
sizer->Add(glPane, 1, wxEXPAND);
frame->SetSizer(sizer);
frame->SetAutoLayout(true);
frame->Show();
ccApp = new AppDelegate();
ccApp->setGLCanvas(glPane);
ccApp->applicationDidFinishLaunching();
When I run, a wxFrame (window) is shown and the app crashes immediately.
Call stack :
libcocos2d.dll!cocos2d::Renderer::setupVBO() Line 352 C++
libcocos2d.dll!cocos2d::Renderer::setupBuffer() Line 285 C++
libcocos2d.dll!cocos2d::Renderer::initGLView() Line 272 C++
libcocos2d.dll!cocos2d::Director::setOpenGLView(cocos2d::GLView * openGLView) Line 394 C++
editor.exe!AppDelegate::applicationDidFinishLaunching() Line 44 C++
editor.exe!EditorApp::OnInit() Line 25 C++
wxbase30ud_vc_custom.dll!wxAppConsoleBase::CallOnInit() Line 93 C++
wxbase30ud_vc_custom.dll!wxEntryReal(int & argc, wchar_t * * argv) Line 479 C++
wxbase30ud_vc_custom.dll!wxEntry(int & argc, wchar_t * * argv) Line 188 C++

Related

A class member function calling from other class member function (arduino)

I am developing nema stepper motor driver interfacing with arduino. I created a class. I named it Axis. I want to create an axis object for each motor with this class. but, I can not call the attachInterrupt function in the arduino.h from the class. It return this error :
In member function 'void Axis::setReverseInt()':
parsstep.cpp:12:77: error: invalid use of non-static member function 'void Axis::revDirection()'
attachInterrupt(digitalPinToInterrupt(reverseDirPin), revDirection, RISING);
Axis::Axis()
{
}
Axis::~Axis()
{
}
void Axis::setReverseInt () {
attachInterrupt(digitalPinToInterrupt(reverseDirPin), revDirection, RISING);
}
void Axis::setStopInt () {
//attachInterrupt(digitalPinToInterrupt(stopPin), stopMotor, RISING);
}
void Axis::revDirection()
{
dirMode = !dirMode;
}
void Axis::stopMotor()
{
moveMotor = !moveMotor;
}
void Axis::startMotor()
{
moveMotor = true;
}
void Axis::moveStep(int pulseRev, byte laps, boolean dir)
{
digitalWrite(dirPin, dir);
int totalPulse = pulseRev * laps;
for (int i = 0; i < totalPulse; i++)
{
speedValue = map((analogRead(speedPin)), 0, 1023, 2000, 10);
digitalWrite(pulsePin, HIGH);
delayMicroseconds(speedValue);
digitalWrite(pulsePin, LOW);
delayMicroseconds(speedValue);
}
}
I faced a similar issue when writing my own libraries for a big project I've been working on. It happened to me while configuring interrupts inside a method. I found a workaround that while it might seem a bit more complex, it does the trick. In my case, I used it for reading a rotary encoder its switch.
What I used is a pointer to an ISR handler and a method to call it. On your setup function you can call the initialization of the ISR service.
First, in your .h file declare the following methods (in addition to your own):
void init();
static void ISRHandler();
void setupISRHandler(uint8_t pin, void (*ISR)(void), int state); // this will configure your ISR
and a pointer:
static Axis *_ISRPointer; // can be declared as private (like in here from the '_' prefix)
Then, in your .cpp file you use it like this:
#include "Axis.h"
Axis *Axis::_ISRPointer = nullptr;
you will initialize the ISR handler during the setup using the init method:
void Axis::init(){
setupISRHandler(reverseDirPin, Axis::ISRHandler, RISING);
}
the setup method looks like:
void Axis::setupISRHandler(uint8_t pin, void (*ISRFunction)(void), int state){
attachInterrupt(digitalPinToInterrupt(pin), ISRFunction, state);
}
the ISRHandler is in charge of calling the ISR routine:
void Axis::ISRHandler(){
_ISRPointer-> revDirection();
}
void Axis::revDirection(void){
// Do not forget to disable interrupts
cli();
// your routine goes here
dirMode= !dirMode;
sei();
}
Therefore, you will do something like this in your main .ino file:
#include "Axis.h"
#include <whateverotherlibraryyouneed.h>
Axis ax = Axis(); // your new object
void setup(){
ax.init();
ax.startMotor();
}
void loop(){
if(day == SUNNY){
ax.moveStep(100, 2 , CLOCKWISE);
} else {
ax.stopMotor();
}
ax.someOtherMethodToControlTheMotor(anyArgumentYouWant);
}
I hope that my convoluted solution helps
Cheers
Dr. Daniel

Add custom wms layer to codename one Mapcontainer

I am building an android GPS app with Codename one. I use com.codename1.googlemaps.MapContainer to create a Google map;
In my app is use tabs to create different "pages".
Code:
cnt = new MapContainer();
t.addTab("Tab3", cnt);
And for my current location I use:
try {
Coord position = new Coord(lat,lng);
cnt.clearMapLayers();
cnt.setCameraPosition(position);
cnt.addMarker(EncodedImage.create("/maps-pin.png"), position, "Hi marker", "Optional long description", new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// stuff todo...
}
});
} catch(IOException err) {
// since the image is iin the jar this is unlikely
err.printStackTrace();
}
I like to add a wms layer to the Google Maps. Is this possible? I can't find in codenameone a command addLayer. If yes, do you have a code snippet how to do this?
If it is not possiple, can I use openlayers in my codename one app? Can you give me a code snippet to do this?
Edit
I started to create an native file to "catch"the addtileoverlay from google maps api. The layer I want to use is a xyz layer, so I think I can use a urltileprovider from the googlemap api
I made the native code for the tileoverlay but the tileoverlay doesn't appear. Is it because i didn't get a link with the mapcontainer.
I am little bit stuck. I tried to build from scratch with the googmaps example but the mapcompnent is not anymore used.
package com.Bellproductions.TalkingGps;
import com.google.android.gms.maps.model.UrlTileProvider;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.TileOverlayOptions;
import com.google.android.gms.maps.model.TileProvider;
import com.google.android.gms.maps.model.TileOverlay;
import com.codename1.impl.android.AndroidNativeUtil;
import java.util.Locale;
import java.net.MalformedURLException;
public class SeamarksImpl {
private GoogleMap mapInstance;
private TileOverlay m;
TileProvider provider;
public boolean isSupported() {
return true;
}
public long addTilelayer (){
final String URL_FORMAT = "http://t1.openseamap.org/seamark/{z}/{x}/{y}.png";
AndroidNativeUtil.getActivity().runOnUiThread(new Runnable() {
public void run() {
provider = new UrlTileProvider(256, 256) {
#Override
public synchronized URL getTileUrl(int x, int y, int zoom) {
try {
y = (1 << zoom) - y - 1;
return new URL(String.format(Locale.US, URL_FORMAT, zoom, x, y ));
} catch (MalformedURLException e) {
throw new RuntimeException();
}
}
TileOverlayOptions tileopt = new TileOverlayOptions().tileProvider(provider);
public void addlayer() {
m = mapInstance.addTileOverlay(tileopt);
}
};
}
});
long p = 1;
return p;}
}
My seamarks.java file has this code to bind with the native interface
import com.codename1.system.NativeInterface;
/**
*
* #author Hongerige Wolf
*/
public interface Seamarks extends NativeInterface {
public void addTilelayer ();
}
In the mainactivity java file i have the statements
public Seamarks seamark;
public void init(Object context) {
seamark = (Seamarks)NativeLookup.create(Seamarks.class);
}
public void start() {
seamark.addTilelayer();
}
Update
I created a new googlemaps.CN1lib. But the xyz layer is not showing on the googlemaps. I used native code tot use the Tileoverlay feature and tried to add tileoverlay in the same way as Markers.
In the InternalNativeMapsImpl file i changed
private void installListeners() {
/*
if (mapInstance == null) {
view = null;
System.out.println("Failed to get map instance, it seems google play services are not installed");
return;
}*/
view.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(GoogleMap googleMap) {
mapInstance = googleMap;
TileProvider tileProvider;
tileProvider = new UrlTileProvider(256, 256) {
String tileLayer= "http://t1.openseamap.org/seamark/";
#Override
public synchronized URL getTileUrl(int x, int y, int zoom) {
// The moon tile coordinate system is reversed. This is not normal.
int reversedY = (1 << zoom) - y - 1;
//String s = String.format(Locale.US, tileLayer , zoom, x, y);
String s = tileLayer + "/" + zoom + "/" + x + "/" + reversedY + ".png";
URL url = null;
try {
url = new URL(s);
} catch (MalformedURLException e) {
throw new AssertionError(e);
}
return url;
}
};
mMoonTiles = mapInstance.addTileOverlay(new TileOverlayOptions().tileProvider(tileProvider));
mapInstance.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
public boolean onMarkerClick(Marker marker) {
Long val = listeners.get(marker);
if (val != null) {
MapContainer.fireMarkerEvent(InternalNativeMapsImpl.this.mapId, val.longValue());
return true;
}
return false;
}
});
mapInstance.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
public void onCameraChange(CameraPosition position) {
MapContainer.fireMapChangeEvent(InternalNativeMapsImpl.this.mapId, (int) position.zoom, position.target.latitude, position.target.longitude);
}
});
mapInstance.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
public void onMapClick(LatLng point) {
Point p = mapInstance.getProjection().toScreenLocation(point);
MapContainer.fireTapEventStatic(InternalNativeMapsImpl.this.mapId, p.x, p.y);
}
});
mapInstance.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
public void onMapLongClick(LatLng point) {
Point p = mapInstance.getProjection().toScreenLocation(point);
MapContainer.fireLongPressEventStatic(InternalNativeMapsImpl.this.mapId, p.x, p.y);
}
});
mapInstance.setMyLocationEnabled(showMyLocation);
mapInstance.getUiSettings().setRotateGesturesEnabled(rotateGestureEnabled);
}
});
}
Secondly i added a addTilexyz method also in the same way as addMarkers
public long addTilexyz(final String Turl) {
uniqueIdCounter++;
final long key = uniqueIdCounter;
AndroidNativeUtil.getActivity().runOnUiThread(new Runnable() {
public void run() {
TileProvider tileProvider;
tileProvider = new UrlTileProvider(256, 256) {
// Tileurl = "http://t1.openseamap.org/seamark/";
#Override
public synchronized URL getTileUrl(int x, int y, int zoom) {
// The moon tile coordinate system is reversed. This is not normal.
int reversedY = (1 << zoom) - y - 1;
String s = String.format(Locale.US, Turl , zoom, x, reversedY);
URL url = null;
try {
url = new URL(s);
} catch (MalformedURLException e) {
throw new AssertionError(e);
}
return url;
}
};
mMoonTiles = mapInstance.addTileOverlay(new TileOverlayOptions().tileProvider(tileProvider));
}
});
return key;
}
In the InternalNativeMaps file i added te method
public long addTilexyz(String Turl);
And in the Mapcontainer file i added
public MapObject addTilexyz(String Turl) {
if(internalNative != null) {
MapObject o = new MapObject();
Long key = internalNative.addTilexyz(Turl);
o.mapKey = key;
markers.add(o);
return o;
} else {
}
MapObject o = new MapObject();
return o;
}
I am puzzeled what is wrong with the code. I wonder if the commands
Long key = internalNative.addTilexyz(Turl);
and
mMoonTiles = mapInstance.addTileOverlay(new TileOverlayOptions().tileProvider(tileProvider));
put the tileoverlay on the googlemap. Or is the tileurl wrong. http://t1.openseamap.org/seamark/z/x/y.png is correct.
We don't expose layers in the native maps at this time, you can fork the project and just add an API to support that to the native implementations.

AdMob Interstitial Cocos2d-x WP8

Can enyone tell me how to call AdMob Interstitial between scenes in my cocos2d-x game?
I have tried this http://robwirving.com/2014/07/21/calling-c-methods-c-winrt-components/ guide, but i don't know how to run it from cocos classes.
Is there any another ways, or some guides?
I've recently made it. You have to do few things. First of all create helper class, which will help you calling native function (I use this for all 3 platforms, but here's just windows phone):
NativeHelper.h:
#ifndef __NATIVE_HELPER_H_
#define __NATIVE_HELPER_H_
#include <string>
#include <functional>
#include "cocos2d.h"
using namespace std;
USING_NS_CC;
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
namespace cocos2d
{
public delegate void CSharpShowInterstitialDelegate();
public ref class WP8NativeEventHelper sealed
{
public:
void WP8NativeEventHelper::SetCSharpShowInterstitialDelegate(CSharpShowInterstitialDelegate^ delegate){
m_CSharpShowInterstitialDelegate = delegate;
}
void CallShowInterstitial();
private:
property static CSharpShowInterstitialDelegate^ m_CSharpShowInterstitialDelegate;
};
}
#endif
class NativeHelper
{
public:
static void showInterstitial(string adSdk);
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
WP8NativeEventHelper^ wp8helper;
#endif
//instance required only for setting callback
static NativeHelper* getInstance();
~NativeHelper()
{
instanceFlag = false;
}
private:
static bool instanceFlag;
static NativeHelper* instance;
NativeHelper() {};
};
#endif // __NATIVE_HELPER_H_
So. We have special C++/CX class Wp8NativeEventHelper, which can "talk" with C#. Here we store a delegate.
How it works:
C# is calling SetCSharpShowInterstitialDelegate and passes a delegate to it, which will be remembered in static property.
Then C++\CX can call it using CallShowInterstitial.
Now NativeHelperWP.cpp:
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
#ifndef __NATIVE_HELPER_WP_H_
#define __NATIVE_HELPER_WP_H_
#include "NativeHelper.h"
void WP8NativeEventHelper::CallShowInterstitial(){
if (m_CSharpShowInterstitialDelegate)
{
m_CSharpShowInterstitialDelegate->Invoke();
}
}
bool NativeHelper::instanceFlag = false;
NativeHelper* NativeHelper::instance = NULL;
NativeHelper* NativeHelper::getInstance()
{
if(!instanceFlag){
instance = new NativeHelper();
instanceFlag = true;
instance->wp8helper = ref new WP8NativeEventHelper();
}
return instance;
}
void NativeHelper::showInterstitial(){
NativeHelper::getInstance()->wp8helper->CallShowInterstitial();
}
#endif
#endif
Here is just an implementation of CallShowInterstitial. Also in NativeHelper::showInterstitial we're calling C++/CX, which later calls c#.
Now c# code (MainPage.xaml.cs):
outside namespace:
using GoogleAds;
inside class:
private InterstitialAd interstitialAd;
in constructor:
WP8NativeEventHelper helper = new WP8NativeEventHelper();
helper.SetCSharpShowInterstitialDelegate(showInterstitial);
and also create showInterstitial function:
public void showInterstitial() //we recreate interstitial each time, because otherwise it'll show only once, only new requests won't work
{
interstitialAd = new InterstitialAd("MY_AD_UNIT_ID");
AdRequest adRequest = new AdRequest();
#if DEBUG
// Enable test ads.
adRequest.ForceTesting = true;
#endif
interstitialAd.ReceivedAd += OnAdReceived;
interstitialAd.LoadAd(adRequest);
}
and finally OnAdReceived:
private void OnAdReceived(object sender, AdEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Ad received successfully");
interstitialAd.ShowAd();
}
Follow this guide to setup admob: https://developers.google.com/mobile-ads-sdk/docs/admob/wp/quick-start
Now let's use this.
In HelloWorldScene.h add:
#include "NativeHelper.h"
In HelloWorldScene.cpp:
NativeHelper::showInterstitial();
The same way you can show/hide/change position of admob banner for example (however it's buggy so I'm using ad mediation).

A cocos2d-x code that cause crash on Android but not on iOS

While porting an cocos2d-x project from iOS to Android, I found a problem that will cause crashing on Android but not on iOS, to show this problem, I made a small modification to the HelloWorld sample. To reproduce this problem, just press the close button on the bottom-right corner, on Android it will crash but not on iOS.
The code that cause the crashing is:
void TestNode::test()
{
// This will cause crash on Android, but OK on iOS
CCCallFunc *selector = CCCallFunc::create(this, callfunc_selector(TestNode::destroy));
this->runAction(selector);
// This is ok on both Android and iOS
// CCCallFunc *selector = CCCallFunc::create(scene_, callfunc_selector(HelloWorld::destroyNode));
// scene_->runAction(selector);
// This is ok on both Android and iOS
// destroy();
}
The complete code as the following:
HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
class TestNode : public cocos2d::CCNode {
public:
TestNode(cocos2d::CCLayer *scene);
~TestNode();
void test();
void destroy();
cocos2d::CCLayer *scene_;
cocos2d::CCSprite *sprite_;
};
class HelloWorld : public cocos2d::CCLayer
{
private:
TestNode *node_;
public:
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::CCScene* scene();
// a selector callback
void menuCloseCallback(CCObject* pSender);
// implement the "static node()" method manually
CREATE_FUNC(HelloWorld);
void destroyNode();
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp:
#include "HelloWorldScene.h"
#include "AppMacros.h"
USING_NS_CC;
TestNode::TestNode(cocos2d::CCLayer *scene):
scene_(scene)
{
sprite_ = CCSprite::create("CloseNormal.png");
sprite_->setPosition(ccp(200, 200));
scene_->addChild(sprite_, 255);
}
TestNode::~TestNode()
{
scene_->removeChild(sprite_, true);
scene_->removeChild(this, true);
CCLog("+++ ~TestNode");
}
void TestNode::test()
{
// This will cause crash on Android, but OK on iOS
CCCallFunc *selector = CCCallFunc::create(this, callfunc_selector(TestNode::destroy));
this->runAction(selector);
// This is ok on both Android and iOS
// CCCallFunc *selector = CCCallFunc::create(scene_, callfunc_selector(HelloWorld::destroyNode));
// scene_->runAction(selector);
// This is ok on both Android and iOS
// destroy();
}
void TestNode::destroy()
{
CCLog("+++ destroy");
delete this;
}
CCScene* HelloWorld::scene()
{
// 'scene' is an autorelease object
CCScene *scene = CCScene::create();
// 'layer' is an autorelease object
HelloWorld *layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
/////////////////////////////
// 2. add a menu item with "X" image, which is clicked to quit the program
// you may modify it.
// add a "close" icon to exit the progress. it's an autorelease object
CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(HelloWorld::menuCloseCallback));
pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2 ,
origin.y + pCloseItem->getContentSize().height/2));
// create menu, it's an autorelease object
CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
pMenu->setPosition(CCPointZero);
this->addChild(pMenu, 1);
/////////////////////////////
// 3. add your codes below...
// add a label shows "Hello World"
// create and initialize a label
CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", TITLE_FONT_SIZE);
// position the label on the center of the screen
pLabel->setPosition(ccp(origin.x + visibleSize.width/2,
origin.y + visibleSize.height - pLabel->getContentSize().height));
// add the label as a child to this layer
this->addChild(pLabel, 1);
// add "HelloWorld" splash screen"
CCSprite* pSprite = CCSprite::create("HelloWorld.png");
// position the sprite on the center of the screen
pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
// add the sprite as a child to this layer
this->addChild(pSprite, 0);
node_ = new TestNode(this);
this->addChild(node_);
return true;
}
void HelloWorld::menuCloseCallback(CCObject* pSender)
{
// CCDirector::sharedDirector()->end();
if (node_) {
node_->test();
node_ = NULL;
}
//#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
// exit(0);
//#endif
}
void HelloWorld::destroyNode()
{
node_->destroy();
}
I don't know why this works on iOS but you should not do this anyway. When you run the code
CCCallFunc *selector = CCCallFunc::create(this, callfunc_selector(TestNode::destroy));
this->runAction(selector);
which calls the function
void TestNode::destroy()
{
CCLog("+++ destroy");
delete this;
}
you are destroying an object that the CCCallFunc action still keeps a pointer to. Then when the CCCallFunc object is destroyed, it runs the CC_SAFE_RELEASE macro which will call release on your TestNode object. However, by now that TestNode object is already deallocated and that is most likely the reason for your crash.

CCBMemberVariableAssigner callbacks disfunctional for custom CCTableViewCell in CocosBuilder

my goal is to design a CCTableViewCell via CocosBuilder
and load it via CCBReader.
My steps so far:
I added a new non-fullscreen CCNode TestCell.ccb in cocosbuilder,
set the root's custom class to TestCell
and added a CCSprite as the roots child while setting
it's doc-root-var to bg.
My problem: after implementing a loader TestCellLoader
as well as the Cell TestCell the callback-function
TestCell::onAssignCCBMemberVariable is not called at all.
What I tried:
Using a CCLayerLoader instead of a CCNodeLoader worked for me so far,
this is the first time I'm using a CCNodeLoader so maybe I missed
a crucial point.
Thank you,
Ciao!
Ben
Here are the codes:
TestCellLoader.h
#include <cocos2d.h>
#include "cocos-ext.h"
#include "TestCell.h"
using namespace cocos2d;
using namespace cocos2d::extension;
class TestCellLoader : public CCNodeLoader
{
public:
CCB_STATIC_NEW_AUTORELEASE_OBJECT_METHOD(TestCellLoader, create);
protected:
CCB_VIRTUAL_NEW_AUTORELEASE_CREATECCNODE_METHOD(TestCell);
virtual CCNode* loadCCNode(CCNode *, CCBReader * pCCBReader);
};
TestCellLoader.cpp
#include "TestCellLoader.h"
CCNode * TestCellLoader::loadCCNode(CCNode * pParent, CCBReader * pCCBReader)
{
CCLOG("TestCell::loadCCNode");
CCNode * ccNode = this->createCCNode(pParent, pCCBReader);
return ccNode;
}
TestCell.h
class TestCell : public CCTableViewCell, public CCNodeLoaderListener, public CCBMemberVariableAssigner
{
public:
TestCell();
virtual ~TestCell();
static TestCell *create();
virtual bool init();
virtual bool initWithBG(CCSprite* bg);
static TestCell* cellWithBG(CCSprite* bg);
// ccbuilder callbacks
virtual bool onAssignCCBMemberVariable(cocos2d::CCObject * pTarget, const char * pMemberVariableName, cocos2d::CCNode * pNode);
virtual void onNodeLoaded(cocos2d::CCNode * pNode, cocos2d::extension::CCNodeLoader * pNodeLoader);
private:
CC_PROPERTY(CCSprite*, bg, Bg);
};
TestCell.m
#include "TestCell.h"
using namespace cocos2d;
using namespace cocos2d::extension;
TestCell::TestCell(){}
TestCell::~TestCell(){}
#pragma mark creation
TestCell* TestCell::create(){
TestCell *pRet = new TestCell();
pRet->init();
pRet->autorelease();
return pRet;
}
bool TestCell::init(){
return true;
}
bool TestCell::initWithBG(CCSprite* bg){
return true;
}
TestCell* TestCell::cellWithBG(CCSprite* bg){
return new TestCell;
}
#pragma mark - synthesize
void TestCell::setBg(cocos2d::CCSprite *sprite){
this->bg = sprite;
}
CCSprite* TestCell::getBg(){
return this->bg;
}
#pragma mark - ccbuilder callbacks
void TestCell::onNodeLoaded(cocos2d::CCNode * pNode, cocos2d::extension::CCNodeLoader * pNodeLoader)
{
CCLOG("TestCell::onNodeLoaded");
}
bool TestCell::onAssignCCBMemberVariable(CCObject* pTarget, const char* pMemberVariableName, CCNode* pNode)
{
CCLOG("TestCell::onAssignCCBMemberVariable %s", pMemberVariableName);
return false;
}
I guess you used a CCLayer as TestCell.ccb's Root object type. Since when you are creating a new ccb file, CCLayer is as default option.
And this is why you use a CCLayerLoader instead of a CCNodeLoader worked.
So change your TestCell.ccb's Root object type to CCNode, this maybe works.