cocos2d-x キーボード入力時画面がせり上がるのを阻止する

cocos2d-x初期設定でキーボード制御がされていて

AndroidManifest.xmlに下記等キーボード制御設定しても無効になる

android:windowSoftInputMode="adjustNothing"

 

修正箇所は

SampleGame\cocos2d\cocos\platform\android\java\src\org\cocos2dx\lib\Cocos2dxActivity.java

 onCreate内にある下記をコメントアウトする

Window window = this.getWindow();
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

cocos2dx 広告(Admob)を出したり消したりする

最初は表示を行い、課金した時などに非表示になる仕組みを作る

パッケージクラス名、広告Unitはテスト用なので変更してください。

 

Android MainActivity.java

package com.test.SampleGame;

import org.cocos2dx.lib.Cocos2dxActivity;

import android.graphics.Color;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import com.google.android.gms.ads.*;

public class MainActivity extends Cocos2dxActivity {
    private static final String AD_UNIT_ID = "ca-app-pub-****************/**********";//広告ユニットID
    private static AdView adView = null;
    private static Handler mHandler;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler();
        Point p = new Point(0, 0);
        getWindowManager().getDefaultDisplay().getSize(p);
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(p.x, LinearLayout.LayoutParams.WRAP_CONTENT);
        layoutParams.gravity = (Gravity.BOTTOM | Gravity.CENTER);

        adView = new AdView(this);
        adView.setVisibility(View.GONE);
        addContentView(adView, layoutParams);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (adView != null) {
            adView.resume();
        }
    }

    @Override
    protected void onPause() {
        if (adView != null) {
            adView.pause();
        }
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        if (adView != null) {
            adView.destroy();
        }
        super.onDestroy();
    }

    public static void showAdMob() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (adView != null) {
                    adView.setVisibility(View.VISIBLE);
                    if(adView.getAdUnitId()!=AD_UNIT_ID) {
                        adView.setAdUnitId(AD_UNIT_ID);
                        adView.setAdSize(AdSize.BANNER);
                    }
                    adView.setBackgroundColor(Color.BLACK);
                    AdRequest adRequest = new AdRequest.Builder()
                            .addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
                            .addTestDevice("Test Device")
                            .build();
                    adView.loadAd(adRequest);
                }
            }
        });
    }
    public static void removeAdMob(){
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (adView != null) {
                    adView.setVisibility(View.GONE);
                    adView.destroy();
                }
            }
        });
    }
}

 

AppDelegate.hにstaticメソッドを定義

#ifndef  _APP_DELEGATE_H_
#define  _APP_DELEGATE_H_

#include "cocos2d.h"

class  AppDelegate : private cocos2d::Application
{
public:
    AppDelegate();
    virtual ~AppDelegate();

    virtual void initGLContextAttrs();

    virtual bool applicationDidFinishLaunching();

    virtual void applicationDidEnterBackground();

    virtual void applicationWillEnterForeground();

    static void showAdMob();

    static void removeAdMob();
};

#endif // _APP_DELEGATE_H_

 

AppDelegate.cpp下記を追加

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/jni/JniHelper.h"
#endif

 

void AppDelegate::showAdMob()
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    cocos2d::JniMethodInfo methodInfo;
    if (!cocos2d::JniHelper::getStaticMethodInfo(methodInfo, "com/test/SampleGame/MainActivity", "showAdMob", "()V"))
    {
        return;
    }
    methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID);
    methodInfo.env->DeleteLocalRef(methodInfo.classID);
#endif
}
void AppDelegate::removeAdMob()
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    cocos2d::JniMethodInfo methodInfo;
    if (!cocos2d::JniHelper::getStaticMethodInfo(methodInfo, "com/test/SampleGame/MainActivity", "removeAdMob", "()V"))
    {
        return;
    }
    methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID);
    methodInfo.env->DeleteLocalRef(methodInfo.classID);
#endif
}

 

cocos-2dx IGIESW found in whitelist: NO エラー

急にこのエラーが出て ビルドは通るのに、Win32ゲームが起動できなくなった

IGIESW c:\samplegame\proj.win32\debug.win32\samplegame.exe found in whitelist: NOIGIWHW Game c:\samplegame\proj.win32\debug.win32\samplegame.exe found in whitelist: NO'samplegame.exe' (Win32): 'C:\Windows\SysWOW64\winsta.dll' が読み込まれました。PDB ファイルを開けないか、ファイルが見つかりません。

 原因は

2017 4/6のGeforceドライバーアップデート381.65

を入れると下記場所でエラーが出る。

samplegame\cocos2d\cocos\platform\desktop\CCGLViewImpl-desktop.cpp line 379

_mainWindow = glfwCreateWindow(neededWidth, neededHeight, _viewName.c_str(), _monitor, nullptr);

 

 

2017 3/20 の378.92に戻すと正常に動く

 

このままだとグラフィックカードのドライバーがアップデートできないので

Nividiaが早急に対応してくれないと困る・・

 

※追記※

win8.1-universalとwin10プロジェクトのUWPなら最新のドライバでも動作が確認できました
Nividiaが対応しない限り、Windowsでcocos2d-xアプリを作るならwin8.1以降しか今のところ厳しそうです

※さらに追記※

cocos2d-xのアップデートも 何もしてないのに直りました。

なぜなのかは不明ですが・・Win32実行できるようになりました

Windowsアップデートとかかな??

 

cocos2d-x バイナリファイルの入出力

UserDefaultを使いデータを保存すると簡易に書き換えられてしまう。

そこで、バイナリとして保存して その後暗号化したりして保存する

unsigned char *配列などのバイナリデータを書き込む際にはcocos2d::Dataを使う

ファイルからDataに取り込み

FileUtils* fileUtil = FileUtils::getInstance();

Data data = fileUtil->getDataFromFile("ファイルパス");

Dataからunsigned char*配列

unsigned char * uchardata = data->getBytes();

ssize_t size = data->getSize();

unsigned char *配列からDataに格納

Data data;
data.fastSet(uchardata, size);

Dataからファイルへ書き出し

FileUtils* fileUtil = FileUtils::getInstance();
bool ret = fileUtil->writeDataToFile(data,"ファイルパス");

 

基本はこの4つです。

それで作ってみたクラスはこちら

 

ファイル入出力クラス

DataFile.h

#ifndef __DATA_FILE_H__
#define __DATA_FILE_H__

#include "cocos2d.h"
#include "ConfigData.h"

#define CONFIG_FILE_NAME "config.data"

class DataFile
{
public:
    static DataFile* getInstance();
    void destroyInstance();
    bool isConfigFileExist();
    void initFilePath();
    bool createConfigFile();
    const std::string& getConfigFilePath();
    bool loadConfigFile();
    bool writeConfigFile();
protected:
    DataFile();
    virtual ~DataFile();

private:
    static DataFile* _dataFile;
    std::string _configFilePath;
    bool _isFilePathInitialized;
};

#endif

DataFile.cpp

#include "DataFile.h"

using namespace std;

USING_NS_CC;

DataFile* DataFile::_dataFile = nullptr;

DataFile::DataFile()
{
}

DataFile::~DataFile()
{
}

DataFile* DataFile::getInstance()
{
    if (!_dataFile)
    {
        _dataFile = new (std::nothrow) DataFile();
        _dataFile->_configFilePath = string("");
        _dataFile->_isFilePathInitialized = false;
        _dataFile->initFilePath();

        if (!_dataFile->isConfigFileExist() && !_dataFile->createConfigFile())
        {
            return nullptr;
        }
    }

    return _dataFile;
}

void DataFile::destroyInstance()
{
    CC_SAFE_DELETE(_dataFile);
}

bool DataFile::isConfigFileExist()
{
    return FileUtils::getInstance()->isFileExist(_configFilePath);
}

void DataFile::initFilePath()
{
    if (!_isFilePathInitialized)
    {
        _dataFile->_configFilePath += FileUtils::getInstance()->getWritablePath() + CONFIG_FILE_NAME;
        _dataFile->_isFilePathInitialized = true;
    }
}
bool DataFile::createConfigFile()
{
    bool bRet = false;
    FileUtils* fileUtil = FileUtils::getInstance();
    ConfigData* configdata = ConfigData::getInstance();

    return fileUtil->writeDataToFile(configdata->GetData(), _configFilePath);
}
const string& DataFile::getConfigFilePath()
{
    return _configFilePath;
}
bool DataFile::loadConfigFile()
{
    if (!isConfigFileExist())
    {
        return false;
    }
    FileUtils* fileUtil = FileUtils::getInstance();
    Data load = fileUtil->getDataFromFile(_configFilePath);
    ConfigData* cdata = ConfigData::getInstance();
    bool ret = cdata->setData(&load);
    load.fastSet(nullptr, 0);
    return ret;
}
bool DataFile::writeConfigFile()
{
    if (!isConfigFileExist())
    {
        return false;
    }
    FileUtils* fileUtil = FileUtils::getInstance();
    ConfigData* cdata = ConfigData::getInstance();
    Data data = cdata->GetData();
    bool ret = fileUtil->writeDataToFile(data, _configFilePath);
    data.fastSet(nullptr, 0);
    return ret;
}

入出力データの基本クラス

 DataModel.h

#ifndef __DATA_MODEL_H__
#define __DATA_MODEL_H__

#include "cocos2d.h"

class DataModel
{
public:
    void GetData(unsigned char *& data, std::string str);
    void GetData(unsigned char *& data, uint64_t num);
    void GetData(unsigned char *& data, uint32_t num);
    void GetData(unsigned char *& data, uint16_t num);
    void GetData(unsigned char *& data, uint8_t num);
    void GetData(unsigned char *& data, bool flg);
    void setData(unsigned char *& data, std::string * setdata, size_t length);
    void setData(unsigned char *& data, uint64_t * setdata);
    void setData(unsigned char *& data, uint32_t * setdata);
    void setData(unsigned char *& data, uint16_t * setdata);
    void setData(unsigned char *& data, uint8_t * setdata);
    void setData(unsigned char *& data, bool * setdata);
protected:
    DataModel();
    virtual ~DataModel();
};
#endif

DataModel.cpp

#include "DataModel.h"

using namespace std;

USING_NS_CC;

DataModel::DataModel()
{
}

DataModel::~DataModel()
{
}

void DataModel::GetData(unsigned char *& data, std::string str)
{
    size_t length = str.length();
    const char* cchar = str.c_str();
    for (unsigned int i = 0; i < length; i++) {
        data[i] = (unsigned char)cchar[i];
    }
    data += length;
}

//0 - 18446744073709551615
void DataModel::GetData(unsigned char *& data, uint64_t num)
{
    size_t length = 8;
    for (unsigned int i = 0; i < length; i++) {
        data[i] = (unsigned char)( (unsigned char*)&num)[i];
    }
    data += length;
}

//0 - 4294967295
void DataModel::GetData(unsigned char *& data, uint32_t num)
{
    size_t length = 4;
    for (unsigned int i = 0; i < length; i++) {
        data[i] = (unsigned char)( (unsigned char*)&num)[i];
    }
    data += length;
}

//0 - 65535
void DataModel::GetData(unsigned char *& data, uint16_t num)
{
    size_t length = 2;
    for (unsigned int i = 0; i < length; i++) {
        data[i] = (unsigned char)( (unsigned char*)&num)[i];
    }
    data += length;
}

//0 - 255
void DataModel::GetData(unsigned char *& data, uint8_t num)
{
    size_t length = 1;
    for (unsigned int i = 0; i < length; i++) {
        data[i] = (unsigned char)( (unsigned char*)&num)[i];
    }
    data += length;
}

void DataModel::GetData(unsigned char *& data, bool flg)
{
    size_t length = 1;
    if (flg) {
        data[0] = (unsigned char)1;
    }
    else {
        data[0] = (unsigned char)0;
    }
    data += length;
}

void DataModel::setData(unsigned char *& data, std::string * setdata, size_t length)
{
    if (length <= 0) {
        *setdata = "";
        return;
    }
    *setdata = std::string(reinterpret_cast<const char*>(data), length);
    data += length;
}

void DataModel::setData(unsigned char *& data, uint64_t * setdata)
{
    size_t length = 8;
    memcpy(&(*setdata), data, length);
    data += length;
}

void DataModel::setData(unsigned char *& data, uint32_t * setdata)
{
    size_t length = 4;
    memcpy(&(*setdata), data, length);
    data += length;
}

void DataModel::setData(unsigned char *& data, uint16_t * setdata)
{
    size_t length = 2;
    memcpy(&(*setdata), data, length);
    data += length;
}

void DataModel::setData(unsigned char *& data, uint8_t * setdata)
{
    size_t length = 1;
    memcpy(&(*setdata), data, length);
    data += length;
}

void DataModel::setData(unsigned char *& data, bool * setdata)
{
    size_t length = 1;
    memcpy(&(*setdata), data, length);
    data += length;
}

コンフィグデータ

ConfigData.h

#ifndef __CONFIG_DATA_H__
#define __CONFIG_DATA_H__

#include "cocos2d.h"
#include "DataModel.h"

class ConfigData : public DataModel
{
public:
    static ConfigData* getInstance();
    cocos2d::Data GetData();
    bool setData(cocos2d::Data * data);
    std::string language;
    uint8_t bgmVolume;
    uint8_t seVolume;
protected:
    ConfigData();
    virtual ~ConfigData();
private:
    void LoadDefault();
    static ConfigData* _configdata;
};

#endif

ConfigData.cpp

#include "ConfigData.h"

using namespace std;

USING_NS_CC;

ConfigData* ConfigData::_configdata = nullptr;

ConfigData::ConfigData()
{
}

ConfigData::~ConfigData()
{
}

ConfigData* ConfigData::getInstance()
{
    if (!_configdata)
    {
        _configdata = new (std::nothrow) ConfigData();
        _configdata->LoadDefault();
    }
    return _configdata;
}

Data ConfigData::GetData()
{
    size_t size = 0;
    size += sizeof(size_t);
    size += _configdata->language.length();
    size += sizeof(uint8_t);
    size += sizeof(uint8_t);
    unsigned char * uchardata = new unsigned char[size + 1];
    uchardata[size] = '\0';
    unsigned char * udata = uchardata;
    DataModel::GetData(udata, _configdata->language.length());
    DataModel::GetData(udata, _configdata->language);
    DataModel::GetData(udata, _configdata->bgmVolume);
    DataModel::GetData(udata, _configdata->seVolume);

//もし暗号化するなら unchardataとsizeを使ってここでする
    Data datasource;
    datasource.fastSet(uchardata, size);
    return datasource;
}

bool ConfigData::setData(Data * data)
{
    unsigned char * uchardata = data->getBytes();

//もし復号化するなら unchardataとdata->getSize()を使ってここでする
    size_t len;
    DataModel::setData(uchardata, &len);
    DataModel::setData(uchardata, &_configdata->language, len);
    DataModel::setData(uchardata, &_configdata->bgmVolume);
    DataModel::setData(uchardata, &_configdata->seVolume);
    if (_configdata->language != "")
    {
        return true;
    }
    else
    {
        _configdata->LoadDefault();
        return false;
    }
}

void ConfigData::LoadDefault()
{
    _configdata->language = string("");
    _configdata->bgmVolume = 10;
    _configdata->seVolume = 5;
}

言語と音量の保存機能を付けてみました

stringなどの文字を格納する時はサイズが分からないので1つ前に文字サイズを格納するのがコツです

 

使い方

AppDelegate.cpp

bool AppDelegate::applicationDidFinishLaunching()内

コンフィグを読み込みConfigDataに格納する

DataFile *df = DataFile::getInstance();
df->loadConfigFile();

ConfigDataの言語が空(初期状態)だったら 英語をとりあえずいれて、

もし端末の言語が日本語だったら、日本語をいれてファイルに書き込み

ConfigData* cfdata = ConfigData::getInstance();
if (cfdata->language.size()<=0) {
    cfdata->language = "en";
    LanguageType lang = Application::getInstance()->getCurrentLanguage();
    if (lang == LanguageType::JAPANESE) {
        cfdata->language = "ja";
    }
    df->writeConfigFile();
}

 こうする事で初回起動は自分の端末から言語を取得するけど、

その後オプションで言語を変えると、2回目からはオプションで変えた言語を読み込みます

 

出力されたファイル内容

C:\Users\ユーザ名\AppData\Local\アプリ名\config.data

f:id:FALStar:20160811100925j:plain

最初の4バイトに文字のバイト数が格納され、この場合は2

次の2バイトに文字が格納され、

次にBGM音量

次にSE音量

が格納されています。

 

暗号復号について、簡易的に行いたいならこちらのサイトに書かれているxorEncodeが参考になります

cocos2d-xでユーザーデータを暗号化して保存したい – KUDE WORKS

 

例えばこんな感じ。

void DataModel::xorEncode(unsigned char *& data, size_t inputLength, std::string keycode)
{
    const unsigned int keyN = keycode.size();
    unsigned int keyIndex = 0;
    for (unsigned int i = 0; i < inputLength; i++) {
        data[i] ^= keycode[keyIndex];
        keyIndex = (keyIndex + 1) % keyN;
    }
}

 

cocos2d-x BGM再生 効果音再生ライブラリ GameHelper2dx

cocos2d-xで音再生するのに、CRI ADX2とCricket Audioで迷ってましたが、

日本人の製作者様が作られた「GameHelper2dx」という

導入が簡単でサイズが小さいライブラリを導入してみました。

 

Windows環境だとプロジェクトに追加して、

改行コードをCRLFに変換したらすぐ使えました

studio.cretia.net

[Cocos2d-x] フェードや区間ループ対応。Audio周りを充実させた自作ライブラリ『GameHelper2dx』 | cretia studio

cocos2d-x UserDefaultのファイル保存先

よく簡易設定保存で使われるUserDefaultですが、

 UserDefault::getXMLFilePath()

このメソッドで保存先が取得できます、

Win32だと

C:\Users\ユーザ名\AppData\Local\プロジェクト名\UserDefault.xml

XML形式で保存されます

 

生のデータが保存されメモ帳で編集できるので、

環境設定ぐらいの情報しか適していないです。

・予期せぬ値が書かれた時にバグが発生しないように設計する必要があります。

 

重要な情報は暗号化して別の方法で保存

cocos2d-x ui::EditBoxを使う Extensionは非推奨 Deprecated

ネットの情報だとEditBoxを使うなら"extensions/cocos-ext.h"を使うと書いてますが、

この方法は古いみたいでui::EditBoxを使う用に修正した

新TestScene.h

#include "cocos2d.h"
#include "ui/CocosGUI.h"

class TestScene : public cocos2d::Layer , public cocos2d::ui::EditBoxDelegate
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    
    CREATE_FUNC(TestScene);
private:
    virtual void editBoxEditingDidBegin(cocos2d::ui::EditBox* editBox);
    virtual void editBoxEditingDidEnd(cocos2d::ui::EditBox* editBox);
    virtual void editBoxTextChanged(cocos2d::ui::EditBox* editBox, const std::string& text);
    virtual void editBoxReturn(cocos2d::ui::EditBox* editBox);
};

 新TestScene.cpp

#include "TestScene.h"

USING_NS_CC;

using namespace ui;
Scene* TestScene::createScene()
{
    Scene* scene = Scene::create();
    Layer* layer = TestScene::create();
    scene->addChild(layer);
    return scene;
}
bool TestScene::init()
{
    if (!Layer::init())
    {
        return false;
    }
    EditBox * editBox = EditBox::create(Size(400, 60), "背景画像");
    editBox->setFont("フォント名", 24);
    editBox->setPlaceHolder("");
    editBox->setFontColor(Color4B(0, 0, 0, 255));
    editBox->setMaxLength(16);
    editBox->setText("");
    editBox->setReturnType(EditBox::KeyboardReturnType::DONE);
    editBox->setInputMode(EditBox::InputMode::SINGLE_LINE);
    editBox->setPosition(Vec2(0, 0));
    editBox->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT);
    editBox->setDelegate(this);
    this->addChild(editBox);
    return true;
}
void TestScene::editBoxEditingDidBegin(EditBox *editBox) {
}

void TestScene::editBoxEditingDidEnd(EditBox *editBox) {
}

void TestScene::editBoxTextChanged(EditBox *editBox, const std::string& text) {
}

void TestScene::editBoxReturn(EditBox *editBox) {
}

EditBoxはBitmapフォントが使えない!!