Can't build project for Android, undefined reference

When I try to run/build my Game in Android Studio I get an error but it works without any error in Xcode. I get the following error in Android Studio:

/Users/lukas/Documents/CircleGame/Classes/AppDelegate.cpp:126: error: undefined reference to 'MenuScene::createScene()'
clang++: error: linker command failed with exit code 1 (use -v to see invocation)

This is the code in AppDelegate.cpp that gives an error:

auto scene = MenuScene::createScene();

This is the code in MenuScene.cpp

#include "MenuScene.h"

USING_NS_CC;


Scene* MenuScene::createScene() {
    return MenuScene::create();
}

bool MenuScene::init() {
    if ( !Scene::init() ) {
        return false;
    }

    ...

and this is MenuScene.h

class MenuScene : public cocos2d::Scene {
public:
    virtual bool init();
    static cocos2d::Scene* createScene();
 
    // implement the "static create()" method manually
    CREATE_FUNC(MenuScene);
    
    ...

What can I do to fix this error?

Show us AppDelegate please…are you including what is necessary for a forward declaration depending upon your needs?

This is my AppDelegate.cpp:

/****************************************************************************
 Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
 
 http://www.cocos2d-x.org
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/

#include "AppDelegate.h"
#include "MenuScene.h"
#include "PluginAdmob/PluginAdMob.h"
#include "PluginShare/PluginShare.h"
#include "PluginSdkboxPlay/PluginSdkboxPlay.h"

// #define USE_AUDIO_ENGINE 1
// #define USE_SIMPLE_AUDIO_ENGINE 1

#if USE_AUDIO_ENGINE && USE_SIMPLE_AUDIO_ENGINE
#error "Don't use AudioEngine and SimpleAudioEngine at the same time. Please just select one in your game!"
#endif

#if USE_AUDIO_ENGINE
#include "audio/include/AudioEngine.h"
using namespace cocos2d::experimental;
#elif USE_SIMPLE_AUDIO_ENGINE
#include "audio/include/SimpleAudioEngine.h"
using namespace CocosDenshion;
#endif

USING_NS_CC;

static cocos2d::Size designResolutionSize = cocos2d::Size(480, 320);
static cocos2d::Size smallResolutionSize = cocos2d::Size(480, 320);
static cocos2d::Size mediumResolutionSize = cocos2d::Size(1024, 768);
static cocos2d::Size largeResolutionSize = cocos2d::Size(2048, 1536);

AppDelegate::AppDelegate() {
}

AppDelegate::~AppDelegate() {
#if USE_AUDIO_ENGINE
    AudioEngine::end();
#elif USE_SIMPLE_AUDIO_ENGINE
    SimpleAudioEngine::end();
#endif
}

// if you want a different context, modify the value of glContextAttrs
// it will affect all platforms
void AppDelegate::initGLContextAttrs(){
    // set OpenGL context attributes: red,green,blue,alpha,depth,stencil,multisamplesCount
    GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8, 0};

    GLView::setGLContextAttrs(glContextAttrs);
}

// if you want to use the package manager to install more packages,  
// don't modify or remove this function
static int register_all_packages(){
    return 0; //flag for packages manager
}

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
        glview = GLViewImpl::createWithRect("CircleGame", cocos2d::Rect(0, 0, designResolutionSize.width, designResolutionSize.height));
#else
        glview = GLViewImpl::create("CircleGame");
#endif
        director->setOpenGLView(glview);
    }
    
    
    // Init sdkbox Plugins
    sdkbox::PluginAdMob::init();
    sdkbox::PluginShare::init();
    sdkbox::PluginSdkboxPlay::init();
    sdkbox::PluginSdkboxPlay::signin();
    
    
    
    // turn on display FPS
//    director->setDisplayStats(true);

    // set FPS. the default value is 1.0/60 if you don't call this
    director->setAnimationInterval(1.0f / 60);

    // Set the design resolution
    glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::FIXED_HEIGHT);
    auto frameSize = glview->getFrameSize();
    // if the frame's height is larger than the height of medium size.
    if (frameSize.height > mediumResolutionSize.height) {
        director->setContentScaleFactor(MIN(largeResolutionSize.height/designResolutionSize.height, largeResolutionSize.width/designResolutionSize.width));
    }
    // if the frame's height is larger than the height of small size.
    else if (frameSize.height > smallResolutionSize.height) {
        director->setContentScaleFactor(MIN(mediumResolutionSize.height/designResolutionSize.height, mediumResolutionSize.width/designResolutionSize.width));
    }
    // if the frame's height is smaller than the height of medium size.
    else {
        director->setContentScaleFactor(MIN(smallResolutionSize.height/designResolutionSize.height, smallResolutionSize.width/designResolutionSize.width));
    }

    register_all_packages();

    // create a scene. it's an autorelease object
    auto scene = MenuScene::createScene();

    // run
    director->runWithScene(scene);

    return true;
}

// This function will be called when the app is inactive. Note, when receiving a phone call it is invoked.
void AppDelegate::applicationDidEnterBackground() {
    Director::getInstance()->stopAnimation();

#if USE_AUDIO_ENGINE
    AudioEngine::pauseAll();
#elif USE_SIMPLE_AUDIO_ENGINE
    SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
    SimpleAudioEngine::getInstance()->pauseAllEffects();
#endif
}

// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground() {
    Director::getInstance()->startAnimation();

#if USE_AUDIO_ENGINE
    AudioEngine::resumeAll();
#elif USE_SIMPLE_AUDIO_ENGINE
    SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
    SimpleAudioEngine::getInstance()->resumeAllEffects();
#endif
}

Now post all of MenuScene.

Which build type are you using, cmake or ndk-build? It’ll be defined in proj.android/gradle.properties as PROP_BUILD_TYPE.

If you’re using cmake, then I can only assume that you forgot to add references to the MenuScene.cpp/.h in the CMakeLists.txt that’s in the root folder of your game, such as this section:

list(APPEND GAME_SOURCE
     Classes/AppDelegate.cpp
     Classes/HelloWorldScene.cpp
     )
list(APPEND GAME_HEADER
     Classes/AppDelegate.h
     Classes/HelloWorldScene.h
     )

If you’re using ndk-build (better to use cmake instead of this), then you need to update proj.android/app/jni/Android.mk with the source references, where you see this:

LOCAL_SRC_FILES := $(LOCAL_PATH)/hellocpp/main.cpp \
                   $(LOCAL_PATH)/../../../Classes/AppDelegate.cpp \
                   $(LOCAL_PATH)/../../../Classes/HelloWorldScene.cpp

I assume that in Xcode, you created the class files within the IDE, and it automatically added them to the Xcode project, which is why it works in there. If you stick with CMake, then you won’t be doubling up your work, because the CMakeLists.txt is the only place you’ll ever need to add the references, and it will work for all platforms (iOS, Android, Windows etc).

1 Like

@R101 OMG, good point! I forgot OP could be using CMake :slight_smile:

Yep it’s a strange error given the source the OP posted is including the header correctly, so it is more likely to be something to do with the build files.

I also thought header guards, but if they didn’t change them (say copying another .h as a template) they would be getting a duplicate symbol error.

Ooh I found my mistake. It’s in my Android.mk file:

LOCAL_SRC_FILES := \
/Users/lukas/Documents/CircleGame/Classes/AppDelegate.cpp \
# $(LOCAL_PATH)/hellocpp/main.cpp \
/Users/lukas/Documents/CircleGame/Classes/MenuScene.cpp \
/Users/lukas/Documents/CircleGame/Classes/GameScene.cpp \
/Users/lukas/Documents/CircleGame/Classes/ShopScene.cpp \
/Users/lukas/Documents/CircleGame/Classes/Diamond.cpp \
/Users/lukas/Documents/CircleGame/Classes/Globals.cpp \
/Users/lukas/Documents/CircleGame/Classes/Sounds.cpp \
/Users/lukas/Documents/CircleGame/Classes/AdListener.cpp

I had to take out the comment “# $(LOCAL_PATH)/hellocpp/main.cpp \” because it somehow messed up the code and the files after that line didn’t get added. So now that this error is fixed, i’m getting a few other errors:

In file included from /Users/lukas/Documents/CircleGame/Classes/Sounds.cpp:8:
/Users/lukas/Documents/CircleGame/proj.android/app/jni/../../../Classes/Sounds.h:16:27: error: no type named 'string' in namespace 'std'
    static void play(std::string name);
                     ~~~~~^
/Users/lukas/Documents/CircleGame/Classes/Sounds.cpp:12:1: error: C++ requires a type specifier for all declarations
USING_NS_CC;
^
/Users/lukas/Documents/CircleGame/Classes/Sounds.cpp:25:14: error: out-of-line definition of 'play' does not match any declaration in 'Sounds'
void Sounds::play(std::string name){
             ^~~~
/Users/lukas/Documents/CircleGame/Classes/Sounds.cpp:26:9: error: use of undeclared identifier 'UserDefault'
    if (UserDefault::getInstance()->getBoolForKey("isSoundOn")){
        ^
4 errors generated.

I don’t get why it works for iOS but not for Android, it’s so weird. These errors make no sense. I’m pretty sure that my code is correct. If it helps, here is my full Android.mk file:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := MyGame_shared

LOCAL_MODULE_FILENAME := libMyGame

LOCAL_SRC_FILES := \
/Users/lukas/Documents/CircleGame/Classes/AppDelegate.cpp \
/Users/lukas/Documents/CircleGame/Classes/MenuScene.cpp \
/Users/lukas/Documents/CircleGame/Classes/GameScene.cpp \
/Users/lukas/Documents/CircleGame/Classes/ShopScene.cpp \
/Users/lukas/Documents/CircleGame/Classes/Diamond.cpp \
/Users/lukas/Documents/CircleGame/Classes/Globals.cpp \
/Users/lukas/Documents/CircleGame/Classes/Sounds.cpp \
/Users/lukas/Documents/CircleGame/Classes/AdListener.cpp

LOCAL_CPPFLAGS := -DSDKBOX_ENABLED \
-DSDKBOX_COCOS_CREATOR
LOCAL_LDLIBS := -landroid \
-llog
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../Classes

# _COCOS_HEADER_ANDROID_BEGIN
# _COCOS_HEADER_ANDROID_END

LOCAL_WHOLE_STATIC_LIBRARIES += PluginSdkboxAds
LOCAL_WHOLE_STATIC_LIBRARIES += sdkbox
LOCAL_WHOLE_STATIC_LIBRARIES += PluginAdMob
LOCAL_WHOLE_STATIC_LIBRARIES += PluginShare
LOCAL_WHOLE_STATIC_LIBRARIES += PluginSdkboxPlay

LOCAL_STATIC_LIBRARIES := cc_static

# _COCOS_LIB_ANDROID_BEGIN
# _COCOS_LIB_ANDROID_END

include $(BUILD_SHARED_LIBRARY)
$(call import-add-path, $(LOCAL_PATH))

$(call import-module, cocos)
$(call import-module, ui)
$(call import-module, ./sdkbox)
$(call import-module, ./PluginSdkboxAds)
$(call import-module, ./PluginAdmob)
$(call import-module, ./PluginShare)
$(call import-module, ./PluginSdkboxPlay)

# _COCOS_LIB_IMPORT_ANDROID_BEGIN
# _COCOS_LIB_IMPORT_ANDROID_END

try #include <string> for the std::string error. Not sure about the others, make sure the imports are correct. I have had some similar weird issues on Android that did not occur on iOS. @R101 any follow up on that?

I already have that :confused: so weird

Can you please create a new project in a new folder using the following:

cocos new gamename -p com.example.gamename -l cpp -d .

Do not add anything from SDKBOX to this new project. Double check that it has the same setting for PROP_BUILD_TYPE in gradle.properties. Open it up in Android Studio, and build it as is. It should work, and if not, then something is not right with your development setup.

Aside from that, I strongly recommend you move to using cmake for your builds, especially since your project doesn’t seem to have many files at the moment, so there’s is very little to do in order to get it moved to cmake. There is really no point in using the old ndk-build system. Once you do that, you no longer have to care about the Android *.mk files etc.

Is your Sounds.cpp including the cocos2d.h header:

#include "cocos2d.h"

That is one possible reason for this error.

$(LOCAL_PATH)/hellocpp/main.cpp is a required file, and you shouldn’t just remove it without checking what it’s contents are. It contains the entry point to your native application, so removing it means your app won’t work.

I created a new empty Game, updated the build type to NDK and I can run it for iOS and Android. So the issue is not in my development setup.

In my actual game:
I added the line $(LOCAL_PATH)/hellocpp/main.cpp back into my Android.mk file. Still same errors.
I then added #include "cocos2d.h" to all my .cpp files because I only had it in my .h files before. But now when I run the game in Android Studio I get a new error:

Error: Program type already present: org.cocos2dx.lib.GameControllerAdapter$4

Well, another thing you also know is that it’s an issue specifically with the build files in your problematic project, since the test project worked fine. So, now what I recommend you do is either compare the differences between project files between your main project and the test project, or simply start copying files from main project to your test project, build, then add more, and build again, until you come across the issue. Those are only a few ways to narrow down what is going on.

If you already include cocos2d.h in the header file, then the equivalent CPP file won’t need a reference to it. For instance, if your Sounds.h has the #include "cocos2d.h", then your Sounds.cpp doesn’t need the same line in there, as long as it includes Sounds.h.

Since this issue seems to have something to do with your build files, whether it’s in your gradle files or your *.mk files, there is no way to tell, you need to strip your main project back (remove SDKBOX for example), and then try to compile it. It could also be that you need to delete the build folder before you try again, because Android Studio is known to completely make a mess of things and give you strange errors all because of the existing build output files.

I deleted all sdkbox related code from my class files and copied them to the new empty project that I created for testing. The game now works on both iOS and Android but without any sdkbox plugins. Then I added the sdkbox plugins and code back to the game one by one. After adding Admob it still worked fine. Then I added SdkboxPlay and now the game instantly crashes in Android but still works for iOS. How do I fix this?