반응형

schedule는 많은 곳에서 사용하게 됩니다.

특히 반복적으로 수행해야 하는 곳에서 사용하게 되는데

예를들어 게임내에 시계를 표시해주어야 한다고 가정 했을 때

1초에 한번씩 반복적으로 호출 되면 좋겠죠?


그럴때 사용하는것이 schedule 입니다.

schedule를 사용하는 방법에는 여러가지가 있지만 그중에서 2가지만 사용해 보도록 하겠습니다.

첫번째 사용자가 반복되는 시간을 마음대로 조절할 수 있는 방법 입니다.


[사용자가 원하는대로 호출]

bool HelloWorld::init()

{

    //////////////////////////////

    // 1. super init first

    if ( !Layer::init() )

    {

        return false;

    }

    

    this->_time = 100.0f;

    

    this->schedule(schedule_selector(HelloWorld::gameTime), 1.0f);

    

    return true;

}


1초마다 한번씩 gameTime을 호출 합니다.


void HelloWorld::gameTime(float dt)

{

    --this->_time;

    CCLOG("Game Time: %d", (int)this->_time);

    if (this->_time <= 0) {

        CCLOG("Game Over!");

        this->unschedule(schedule_selector(HelloWorld::gameTime));

    }

}


호출 될때마다 dt만큼 감소시켜주고 0이 되면 스케줄을 중지 합니다.



[매 프레임 마다 호출]

bool HelloWorld::init()

{

    //////////////////////////////

    // 1. super init first

    if ( !Layer::init() )

    {

        return false;

    }

    

    this->_time = 100.0f;

 

    this->scheduleUpdate();


    return true;

}


기본적으로 모든 노드에는 update함수가 있으며 이를 오버라이드 해주면 된다.


    virtual void update(float dt) override;


노드에 있는 update함수를 사용할 때 


void HelloWorld::update(float dt)

{

    this->_time -= dt;

    CCLOG("Game Time: %d", (int)this->_time);

    if (this->_time <= 0) {

        CCLOG("Game Over!");

        this->unscheduleUpdate();

    }

}


동일하게 dt만큼 감소시켜 주면 초당 1씩줄어드는 효과를 볼 수 있다.



둘중 어느 것을 사용해도 되며 

편의를 위해 update를 사용하는 경우가 많습니다.


프레임마다 호출되는 것이 부담을 갈 때는 위에 함수를 사용하여 

원하는 시간마다 호출되게 하는 것이 바람직 합니다.


반응형
반응형

TEXTURE FORMATS SUPPORTED BY COCOS2D-X

FormatAlphaPixel FormatHardware DecodedMipMapAlpha PremultipliedPlatform
PNGARGB888/RGBA8888/I8/IA88N/AN/AYesAll
JPGN/ARGB888/I8N/AN/AN/AAll
WebPARGBA8888N/AN/ANoAll
TIFFARGBA8888N/AN/AYesAll
TGAARGB5A1/RGB888/RGBA8888/I8N/AN/ANoAll
PVRv2APVRTC2/PVRTC2A/PVRTC4/PVRTC4APowerVR GPUANoiOS
PVRv3APVRTC2/PVRTC2A/PVRTC4/PVRTC4APowerVR GPUAFlag DependentiOS
ETC1N/ARGB888GLES 2.0 GPUN/AN/AAll(SD)
ATITCAATC_RGB/ATC_EXPLICIT_ALPHA/ATC_INTERPOLATED_ALPHAAdreno GPUANoAll(SD)
S3TCAS3TC_DXT1/S3TC_DXT3/S3TC_DXT5YANoAll(SD)

Note:
A: Available
N/A: Not Available
Yes: Available and Enabled
No: Available and Disabled
SD: Soft Decoder on unsupported platforms

  • The hardware decoded devices may supported more than what listed in the table. Please refer to Configure::support* to check if it is available on the running device.
  • PVR supports more pixel formats in practice, but we are just listing the most common types.
  • For software decoder, the files are always loaded into Texture2D with RGBA8888 decoded.


왠만한 이미지들은 cocos2d-x에서 모두 지원합니다.

저거 이외에 이미지를 로드해야할 이유는 크게 없다고 보는데

가끔 유니티 사용하는 디자이너들이 PSD파일이 바로 로드 되니깐 

즉시 사용하는 경우가 종종 있는데 좋은 방법은 아닌것 같습니다.


반응형
반응형


로그인과 같은 기능을 추가할 때 EditBox를 사용하는데 

안드로이드는 Back키가 있어 키보드를 내릴 수 있지만 iOS는 키보드를 내릴 방법이 없습니다.

간혹 오픈되어 있는 앱들을 설치하여 사용할 때도 iOS같은 경우 키보드를 내리지 못해 다음 단계로 

넘어가지 못한 다던가 그러한 문제가 발생하여 유저들로 부터 깊은 빡침을 받게 합니다.


유저들은 이런거 못참습니다. 

바로 삭제 해버리죠 ^^


스토어에 삭제 수가 점점 늘어 나길래 원인 분석을 하다보니 이와 같은 문제가 있다고 판단하여 

강제로 키보드를 내리는 방법을 구글링으로 찾아 보았으나 좋은 방법을 찾진 못했습니다.

그래서 엔진 쪽을 살펴 보다보니 좋은 방법이 떠올라 글 남깁니다.


엔진 쪽을 자세히 보니 ui::EditBox를 살펴보니 키보드를 활성화 시키는 방법은 있었습니다.

UIEditBox.cpp 파일을 보면 다음과 같은 부분이 있습니다.


1
2
3
4
5
6
7
void EditBox::touchDownAction(Ref *sender, TouchEventType controlEvent)
{
    if (controlEvent == Widget::TouchEventType::ENDED) {
        _editBoxImpl->openKeyboard();
    }
}
 
cs


touchDownAction에 EventType을 ENDED를 주면 키보드를 강제 활성화 시킵니다.

사용법은 아래와 같이 touchDownAction 함수를 호출해주면 됩니다.

1
m_EditBox->touchDownAction(m_EditBox, cocos2d::ui::Widget::TouchEventType::ENDED);
cs
< Keyboard Open >


하지만 아무리 찾아 봐도 키보드를 Close하는 부분은 빠져 있습니다.
cocos2d쪽에서 다른 문제가 생길 수 있어 해놓지 않은 것 인지 
아직 이런 문제를 파악하지 못해 수정을 안한 것인지...
버전업이 되다 보면 알 수 있겠내요. ^^


그래서 키보드를 Close하는 부분을 소스에 간단하게 추가해봤습니다.
1
2
3
4
5
6
7
8
void EditBox::touchDownAction(Ref *sender, TouchEventType controlEvent)
{
    if (controlEvent == Widget::TouchEventType::ENDED) {
        _editBoxImpl->openKeyboard();
    } else if (controlEvent == Widget::TouchEventType::CANCELED) {
        _editBoxImpl->closeKeyboard();
    }
}
cs

EventType을 CANCELED로 주면 키보드를 강제로 비활성화 하도록 수정했습니다.


touchDownAction를 호출할 때 Event Type을 CANCELED로 주면 키보드가 Close 됩니다.


1
m_EditBox->touchDownAction(m_EditBox, cocos2d::ui::Widget::TouchEventType::CANCELED);
cs

< Keyboard Close >



엔진을 수정하여 사용하게 되면 나중에 어떤 문제가 생기거나 엔진 버전 업이 될 경우 손이 많이 가게 됩니다.

수정한 부분을 간혹 잊어 버리고 엔진을 덮어 써버릴 경우 백업을 하지 않았다면 소스가 날아가 버립니다. ㅠ

하지만 이런 어쩔수 없는 상황에서는 수정해서 사용할 수 밖에 없습니다.



반응형
반응형

cocos2d-x를 이용하여 Youtube동영상을 재생하는 웹화면을 개발 중인데 

cocos2d-x 3.3 이상부터 Webview를 지원하여 걱정없이 개발 하고 있었습니다.


개발 다 끝내고 iOS테스트 완료 하였습니다.. 

Android 테스트하는데... 좌절하고 말았습니다.


3가지 문제가 발생했습니다.


  1. 유투브 동영상 전체화면을 지원불가.
  2. 동영상 전체화면까지 수정했으나 전체화면에서 가로/세로 화면전환 불가.
  3. Webview를 remove하여도 동영상 사운드 계속 재생.

어찌어찌 짧은 Java실력으로 cocos2dx Java Source 수정하여 3가지 모두 해결보았습니다.

1, 2번 문제 부터 해결해 보도록 하겠습니다. 


cocos2d에 아래 경로로 가시면 Cocos2dxWebView.java 파일이 있습니다.

"프로젝트명"/cocos2d/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxWebView.java

이 파일을 아래의 소스로 교체 합니다.


[ Cocos2dxWebView.java File ]


package org.cocos2dx.lib;

 

import android.annotation.SuppressLint;

import android.app.Activity;

import android.app.ProgressDialog;

import android.content.Context;

import android.content.pm.ActivityInfo;

import android.graphics.Bitmap;

import android.os.Build;

import android.util.Log;

import android.view.Gravity;

import android.view.MotionEvent;

import android.view.View;

import android.view.ViewGroup;

import android.view.Window;

import android.view.WindowManager;

import android.webkit.JsResult;

import android.webkit.WebChromeClient;

import android.webkit.WebSettings;

import android.webkit.WebView;

import android.webkit.WebViewClient;

import android.widget.FrameLayout;

import android.widget.Toast;

 

import java.lang.reflect.Method;

import java.net.URI;

import java.util.concurrent.CountDownLatch;

 

class ShouldStartLoadingWorker implements Runnable {

    private CountDownLatch mLatch;

    private boolean[] mResult;

    private final int mViewTag;

    private final String mUrlString;

    

    ShouldStartLoadingWorker(CountDownLatch latch, boolean[] result, int viewTag, String urlString) {

        this.mLatch = latch;

        this.mResult = result;

        this.mViewTag = viewTag;

        this.mUrlString = urlString;

    }

    

    @Override

    public void run() {

        this.mResult[0] = Cocos2dxWebViewHelper._shouldStartLoading(mViewTag, mUrlString);

        this.mLatch.countDown(); // notify that result is ready

    }

}

 

public class Cocos2dxWebView extends WebView {

    private static final String TAG = Cocos2dxWebViewHelper.class.getSimpleName();

    

    private int mViewTag;

    private String mJSScheme;

    

    public Cocos2dxWebView(Context context) {

        this(context, -1);

    }

    

    @SuppressLint("SetJavaScriptEnabled")

    public Cocos2dxWebView(Context context, int viewTag) {

        super(context);

        this.mViewTag = viewTag;

        this.mJSScheme = "";

        

        this.setFocusable(true);

        this.setFocusableInTouchMode(true);

        

        this.getSettings().setSupportZoom(false);

        this.getSettings().setJavaScriptEnabled(true);

        this.getSettings().setDisplayZoomControls(true);

        

        // `searchBoxJavaBridge_` has big security risk. http://jvn.jp/en/jp/JVN53768697

        try {

            Method method = this.getClass().getMethod("removeJavascriptInterface", new Class[]{String.class});

            method.invoke(this, "searchBoxJavaBridge_");

        } catch (Exception e) {

            Log.d(TAG, "This API level do not support `removeJavascriptInterface`");

        }

        

        this.setWebViewClient(new Cocos2dxWebViewClient());

        //this.setWebChromeClient(new WebChromeClient());

 

        // 새로만든 Chromeclient를 추가.

        this.setWebChromeClient(new ChromeClient((Cocos2dxActivity)getContext()));

    }

    

    public void setJavascriptInterfaceScheme(String scheme) {

        this.mJSScheme = scheme != null ? scheme : "";

    }

    

    public void setScalesPageToFit(boolean scalesPageToFit) {

        this.getSettings().setSupportZoom(scalesPageToFit);

    }

    

    class Cocos2dxWebViewClient extends WebViewClient {

        @Override

        public boolean shouldOverrideUrlLoading(WebView view, final String urlString) {

            Cocos2dxActivity activity = (Cocos2dxActivity)getContext();

            

            try {

                URI uri = URI.create(urlString);

                if (uri != null && uri.getScheme().equals(mJSScheme)) {

                    activity.runOnGLThread(new Runnable() {

                        @Override

                        public void run() {

                            Cocos2dxWebViewHelper._onJsCallback(mViewTag, urlString);

                        }

                    });

                    return true;

                }

            } catch (Exception e) {

                Log.d(TAG, "Failed to create URI from url");

            }

            

            boolean[] result = new boolean[] { true };

            CountDownLatch latch = new CountDownLatch(1);

            

            // run worker on cocos thread

            activity.runOnGLThread(new ShouldStartLoadingWorker(latch, result, mViewTag, urlString));

            

            // wait for result from cocos thread

            try {

                latch.await();

            } catch (InterruptedException ex) {

                Log.d(TAG, "'shouldOverrideUrlLoading' failed");

            }

            

            return result[0];

        }

        

        @Override

        public void onPageFinished(WebView view, final String url) {

            super.onPageFinished(view, url);

            Cocos2dxActivity activity = (Cocos2dxActivity)getContext();

            activity.runOnGLThread(new Runnable() {

                @Override

                public void run() {

                    Cocos2dxWebViewHelper._didFinishLoading(mViewTag, url);

                }

            });

        }

        

        @Override

        public void onReceivedError(WebView view, int errorCode, String description, final String failingUrl) {

            super.onReceivedError(view, errorCode, description, failingUrl);

            Cocos2dxActivity activity = (Cocos2dxActivity) getContext();

            activity.runOnGLThread(new Runnable() {

                @Override

                public void run() {

                    Cocos2dxWebViewHelper._didFailLoading(mViewTag, failingUrl);

                }

            });

        }

    }

    

    public void setWebViewRect(int left, int top, int maxWidth, int maxHeight) {

        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,

                                                                             FrameLayout.LayoutParams.WRAP_CONTENT);

        layoutParams.leftMargin = left;

        layoutParams.topMargin = top;

        layoutParams.width = maxWidth;

        layoutParams.height = maxHeight;

        layoutParams.gravity = Gravity.TOP | Gravity.LEFT;

        

        this.setLayoutParams(layoutParams);

    }

    

    // 전체화면 가능한 ChromeClient를 WebVhromeClient를 상속받아 새로 만듬...

    public final class ChromeClient extends WebChromeClient {

        private View mCustomView;

        private Activity mActivity;

        

        public ChromeClient(Activity activity) {

            this.mActivity = activity;

        }

        

        @Override

        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {

            result.confirm();

            return super.onJsAlert(view, url, message, result);

        }

        

        private int mOriginalOrientation;

        private FullscreenHolder mFullscreenContainer;

        private CustomViewCallback mCustomViewCollback;

        

        @Override

        public void onShowCustomView(View view, CustomViewCallback callback) {

            

            if (mCustomView != null) {

                callback.onCustomViewHidden();

                return;

            }

            

            mOriginalOrientation = mActivity.getRequestedOrientation();

            

            FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView();

            

            mFullscreenContainer = new FullscreenHolder(mActivity);

            mFullscreenContainer.addView(view, ViewGroup.LayoutParams.MATCH_PARENT);

            decor.addView(mFullscreenContainer, ViewGroup.LayoutParams.MATCH_PARENT);

            mCustomView = view;

            mCustomViewCollback = callback;

            //mActivity.setRequestedOrientation(mOriginalOrientation);

            

            // 기존의 프로젝트의 Orientation을 따르는 것이 아니라 전체화면일 경우는 Sensor의 의해 Orientation결정

            mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

        }

        

        @Override

        public void onHideCustomView() {

            if (mCustomView == null) {

                return;

            }

            

            FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView();

            decor.removeView(mFullscreenContainer);

            mFullscreenContainer = null;

            mCustomView = null;

            mCustomViewCollback.onCustomViewHidden();

            

            mActivity.setRequestedOrientation(mOriginalOrientation);

        }

        

    }

    

    static class FullscreenHolder extends FrameLayout {

        

        public FullscreenHolder(Context ctx) {

            super(ctx);

            setBackgroundColor(ctx.getResources().getColor(android.R.color.black));

        }

        

        @Override

        public boolean onTouchEvent(MotionEvent evt) {

            return true;

        }

    }

    

}

 


위 소스로 교체시 WebView에서 동영상 전체화면과 가로/세로는 해결 됩니다.


그 다음 WebView종료시 계속 재생되는 사운드가 문제인데 이부분은 아래 파일을 수정해야 합니다.

"프로젝트명"/cocos2d/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxWebViewHelper.java


[ Cocos2dxWebViewHelper.java File ]


public static void removeWebView(final int index) {

    sCocos2dxActivity.runOnUiThread(new Runnable() {

        @Override

        public void run() {

            Cocos2dxWebView webView = webViews.get(index);

            if (webView != null) {

                webViews.remove(index);

                sLayout.removeView(webView);

                

                // webview를 Destory 해야 사운드 재생이 멈춘다.

                webView.destroy();

                webView = null;

            }

        }

    });

}


removeWebView Method를 위와 같이 수정시 WebView를 remove할 경우 사운드 재생이 멈춥니다.

또는 setStopWebView와 같은 Method를 새로 만들어 사운드 재생만을 멈추게 할수도 있습니다.


위 2가지를 수정하면 정상적으로 유투브 동영상을 재생할 수 있습니다.

cocos2d에서 하루 빨리 수정해주었으면 하내요 ^^


그전에 조금이나마 도움이 될 수 있을 것 같아 공유합니다.


근데 더 큰 문제 점이 있었내요.

Android 문제를 해결 하고 마켓에 올렸는데

유투브 동영상을 제 앱에서 바로 실행하면 앱이 거절 당한다는 메시지가 날라 왔습니다.

유투브 동영상으로 수입을 벌면 안된다는 얘기더군요.


결국 유투브 동영상을 제 앱에 웹브라우저 에서 실행하면 안되고 

유투브 사이트로 이동하여 플레이 하는 걸로 변경하여 해결해야 했습니다.


막상 이렇게 해결 하고 나니 제 앱이 쓸모가 없어졌내요. ㅠㅠ

구글이 점점 앱 제한이 커지는 것 같습니다. 


그래도 안전을 위한 구글에 선택이니 믿고 따라야죠. ^^

반응형
반응형

cocos2d-x에서 std::thread를 이용하여  multi thread를 사용하다보면 종종 에러가 나는 경우가 있습니다.

그 이유는 cocos object들이 별도의 thread를 돌리고 있기 때문인데 특히 통신 부분에서 많이 문제가 

발생하는 것 같습니다.


게임 만들다보면 thread를 꼭 써야 하는 예를 들어 로딩화면에서 로딩 프로그레스가 계속 돌면서 이미지를 

로드한다거나 사운드를 로드한다거나 하면 쓰레드를 쓰지 않고 그냥 실행시 로딩 프로그레스가 멈춰 있는

문제가 발생합니다. 


문제가 있는데 그럼 cocos2d는 thread를 쓰지 말라는 건가요?

아닙니다. 그래서 cocos2d-x scheduleer에서 편하게 사용할 수 있도록 만들어 놨습니다.

당연히 mutex도 걸려 있습니다.


저 같은 경우 Json Parsing에 이용하였는데 보통은 Game Loading부분에 많이 이용 합니다.

요즘 TextureCache::addImageAsync 라는 기능이 있어 이미지 로딩부분이 더 편해졌습니다만

그래도 알아보도록 하죠.


#include "HelloWorldScene.h"

 

USING_NS_CC;

 

Scene* HelloWorld::createScene()

{

    // 'scene' is an autorelease object

    auto scene = Scene::create();

    

    // 'layer' is an autorelease object

    auto 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 ( !Layer::init() )

    {

        return false;

    }

    

    Size visibleSize = Director::getInstance()->getVisibleSize();

    Vec2 origin = Director::getInstance()->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

    auto closeItem = MenuItemImage::create(

                                           "CloseNormal.png",

                                           "CloseSelected.png",

                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));

    

    closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,

                                origin.y + closeItem->getContentSize().height/2));

 

    // create menu, it's an autorelease object

    auto menu = Menu::create(closeItem, NULL);

    menu->setPosition(Vec2::ZERO);

    this->addChild(menu, 1);

 

    /////////////////////////////

    // 3. add your codes below...

 

    // add a label shows "Hello World"

    // create and initialize a label

    

    auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);

    

    // position the label on the center of the screen

    label->setPosition(Vec2(origin.x + visibleSize.width/2,

                            origin.y + visibleSize.height - label->getContentSize().height));

 

    // add the label as a child to this layer

    this->addChild(label, 1);

 

    // add "HelloWorld" splash screen"

    auto sprite = Sprite::create("HelloWorld.png");

 

    // position the sprite on the center of the screen

    sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

 

    // add the sprite as a child to this layer

    this->addChild(sprite, 0);

    

    

    std::string data;

    

    // 스케줄러에 Thread로 이용할 함수를 넣어준다.

    // 인자값도 편하게 넘길 수 있어 사용하기 좋다.

    auto scheduler = Director::getInstance()->getScheduler();

    scheduler->performFunctionInCocosThread(CC_CALLBACK_0(HelloWorld::JsonParsingList, this, data));

    

    

    return true;

}

 

 

void HelloWorld::menuCloseCallback(Ref* pSender)

{

    Director::getInstance()->end();

 

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)

    exit(0);

#endif

}

 

void HelloWorld::JsonParsingList(std::string data)

{

    //Json Parsing 작업을 하면 됩니다.

}


주석을 달아 놓았습니다. 분석하시기 편하실 겁니다. 

Json파싱 외에도 여러 부분에 사용할 수 있는데 가장 대표적인 예가 리소스 로딩 부분일 것 같습니다.


코코스가 무료 엔진 중에 정말 잘 만들어진 것 같습니다.

코코스를 계속 사용하다보면 정말 무궁무진한 기능들이 내포 되어 있습니다.

하지만 우리는 cocos2d-x에는 엄청 많은 기능이 있는데 모르고 넘어가는 때가 많습니다.


다음 포스팅은 파일 다운로드에 대해 써볼까 합니다.

3.9버전에서 curl사용을 안하고 Downloader를 만들었더라구요 

사용해 봤는데 쉽고 좋내요 ^^

시간되는대로 올려 보겠습니다.


반응형
반응형

게임 개발시 1개의 이미지 파일을 여러번 사용해야 하는 일이 종종 발생합니다.

예를들어 상점 같은 팝업에 아이템 배경이라던가 또는 버튼의 배경이미지 같은 경우

동일한 이미지를 사용하는데 여러번 파일을 로드해야 합니다.



1) 이미지 파일 로드하여 사용

기본적으로 아래와 같은 방식으로 사용하죠.

1
2
    auto sprite1 = Sprite::create("btn_bg.png");
    this->addChild(sprite1);
cs

위와같이 사용할 경우 여러번 호출 시 파일 입출력이 많이 일어나게 되어 속도영향을 줄 수 있습니다.


2) 이미지 파일 메모리에 로드하여 사용

파일I/O를 줄이기 위해 많이 사용되는 파일들은 아래와 같이 메모리에 로드하여 사용 합니다.

1
2
3
    SpriteFrameCache::getInstance()->addSpriteFrame(Sprite::create("btn_bg.png")->getDisplayFrame(), "btn_bg");
    auto sprite1 = Sprite::createWithSpriteFrameName("btn_bg");
    this->addChild(sprite1);
cs

가장 좋은 방법이긴 하지만 메모리에 이미지를 로드하기 때문에 많은 파일을 로드시 메모리가 많이 늘어 납니다.


그래서  사용이 끝나고 나면 아래와 같이 제거해 주는것이 좋습니다.

1
SpriteFrameCache::getInstance()->removeSpriteFrameByName("btn_bg");
cs

하지만 간혹 빼먹을 수도 있고 귀찮아서 하지 않는 경우도 있습니다.( 머 요즘 하드웨어 성능이 좋으니..허허..)


[ 여기서 잠깐 ]

잠시 안드로이드 앱 마켓 시장에 대해 얘기해보겠습니다. (주관적인 제생각으로)  

안드로이드 시장이 급격하게 변화되고 있습니다.

안드로이드 버전이 벌써 API 23(마시멜로 6.0)까지 출시가 되었습니다.

그러나 구글 통계에 따르면 안드로이드 API레벨 9 ~ 11 (버전 2.3 ~ 3.0)를 사용하는 사용자가 아직도

게임 전체에서 16%나 사용한다는 점입니다.

(4.x 버전사용자가 50%이상으로 가장 많다고 합니다.)


Facebook SDK 4.x 버전을 사용하려면 안드로이드 API 11 이상 필요합니다. (API 10이하는 Crash 발생)

많은 분들이 높은 Facebook SDK버전을 사용하기 위해 안드로이드 2.x 를 버려야 하나 생각하지만... 

결국 페북 3.x버전을 사용 중이 더군요.


그만큼 2.x수요가 높다는걸 알 수 있습니다.

2.x 버전을 사용하는 스마트폰의 성능은 갤럭시2 ~ 갤럭시노트1 정도 수준이하라는 점이죠.

모바일 게임에서 최적화 작업이 리소스를 줄여 50M이하로 용량을 줄이는 작업도 있지만 

속도 개선 및 메모리 관리도 아직은 중요한 것 같습니다. 

[ /여기서 잠깐 ]


3) 이미지파일 로드 한번으로 복사하여 사용하기

파일로드 한번으로 여러개를 복제하여 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    auto sprite1 = Sprite::create("btn_bg.png");
    this->addChild(sprite1);
    
    auto sprite2 = Sprite::createWithSpriteFrame(sprite1->getSpriteFrame());
    this->addChild(sprite2);
 
    auto sprite3 = Sprite::createWithSpriteFrame(sprite1->getSpriteFrame());
    this->addChild(sprite3);
    
    Sprite* sprite[10];
    for (int i = 0; i < 10++i) {
        sprite[i] = Sprite::createWithSpriteFrame(sprite1->getSpriteFrame());
        this->addChild(sprite[i]);
    }
 
    // 원본제거
    sprite1->removeFromParent();
cs

Frame을 복사했기 때문에 sprite1이 소멸되어도 나머지 sprite는 영향을 받지 않습니다.

그리고 가장 편한 점은 모든 Node들은 autorelease() 되기 때문에 추가로 Frame을 제거해야 하는 일도 없습니다.



단 1회 사용하는 이미지는 1번사용해도 무관합니다.

계속 메모리에 상주하며 사용해야 하는 이미지들은 2번째 방법을 사용하면되고

팝업이나 특정부분에서만 사용하는 이미지들은 3번째 방법을 사용하면 좋을 것 같습니다.




반응형
반응형

게임 개발을 하다보면 ScrollView, ListView, TableView는 거의 필수로 들어 갑니다.

cocos2d-x 2.x 부터 ScrollView에 Menu를 붙여 사용할 때  Menu부분을 터치하여 스크롤 하면

스크롤이 안먹는 문제? 가 있었습니다.


3.9버전까지 오면서 수정 안되는걸 보면 cocos애들은 그방법이 더 옳다고 생각하는것 같내요.

현재 나온 ui::Widget에 있는 Button과 ScrollView를 사용하면 정상동작하고 보너스로 스크롤바 까지 보여줍니다.


2.x 버전을 사용하는 분들이나 3.x버전을 사용하고 있지만 기존에 ScrollView를 사용하시는 분들을 위해

Menu 부분을 터치하고 스크롤하여도 스크롤이 동작하는 소스를 공유합니다.


저는 이번 새로운 프로젝트에서 ui::Widget으로 갈아 탔지만 이전 프로젝트까지 잘 사용했던 소스 입니다.


ScrollView를 상속받아 새로 만드시는 분들도 계신데 그 방법보다 Menu를 상속받아 새로 만드는 방법이 간단해서

이 방법으로 소개 합니다.


- cocos2d-x 3.x 버전에서 사용 가능한 소스 입니다. (2.x 버전은 시간나는대로 올리겠습니다.)


[ ScrollMenu.h ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//  cocos2d-x 3.x 버전에서 사용 가능 합니다.
 
//  ScrollMenu.h
//  Solitaire
//
//  Created by Kyung-Min Park on 2015. 4. 6..
 
#ifndef __ScrollMenu__
#define __ScrollMenu__
 
#include <stdio.h>
#include <cocos2d.h>
 
using namespace cocos2d;
 
class ScrollMenu:public Menu
{
public:
    virtual bool onTouchBegan(Touch* touch, Event* event);
    virtual void onTouchEnded(Touch* touch, Event* event);
    virtual void onTouchMoved(Touch* touch, Event* event);
    
    static ScrollMenu* createWithArray(const Vector<MenuItem*>& arrayOfItems);
    static ScrollMenu* createWithItem(MenuItem* item);
    static ScrollMenu* createWithItems(MenuItem *firstItem, va_list args);
    static ScrollMenu* create(MenuItem* item, ...);
 
    bool init();
    bool initWithArray(const Vector<MenuItem*>& arrayOfItems);
    
private:
    bool isTouching;
 
    Vec2 m_touchesStart;
    Vec2 m_touchesEnd;
};
 
#endif /* defined(__ScrollMenu__) */
 
cs



[ ScrollMenu.cpp ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//  cocos2d-x 3.x 버전에서 사용 가능 합니다.
 
//  ScrollMenu.cpp
//  Solitaire
//
//  Created by Kyung-Min Park on 2015. 4. 6..
 
#include "ScrollMenu.h"
 
bool ScrollMenu::init()
{
    return initWithArray(Vector<MenuItem*>());
}
 
bool ScrollMenu::initWithArray( const Vector<MenuItem*>& arrayOfItems )
{
    if (Layer::init()) {
        _enabled = true;
 
        Size sizeVisible = Director::getInstance()->getWinSize();
        
        this->ignoreAnchorPointForPosition(true);
        setAnchorPoint(Vec2(0.5f, 0.5f));
        this->setContentSize(sizeVisible);
        
        setPosition(Vec2(sizeVisible.width/2, sizeVisible.height/2));
        
        int count = 0;
        
        for (auto& item : arrayOfItems) {
            this->addChild(item, count);
            count++;
        }
        
        _selectedItem = nullptr;
        _state = Menu::State::WAITING;
        
        setCascadeColorEnabled(true);
        setCascadeOpacityEnabled(true);
        
        auto touchListener = EventListenerTouchOneByOne::create();
        touchListener->setSwallowTouches(false);
        touchListener->onTouchBegan = CC_CALLBACK_2(ScrollMenu::onTouchBegan, this);
        touchListener->onTouchMoved = CC_CALLBACK_2(ScrollMenu::onTouchMoved, this);
        touchListener->onTouchEnded = CC_CALLBACK_2(ScrollMenu::onTouchEnded, this);
        touchListener->onTouchCancelled = CC_CALLBACK_2(ScrollMenu::onTouchCancelled, this);
        _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
        
        return true;
    }
    return false;
}
 
ScrollMenu* ScrollMenu::createWithArray( const Vector<MenuItem*>& arrayOfItems )
{
    auto ret = new ScrollMenu();
    if (ret && ret->initWithArray(arrayOfItems)) {
        ret->autorelease();
    }
    else {
        CC_SAFE_DELETE(ret);
    }
    return ret;
}
 
ScrollMenu* ScrollMenu::createWithItem( MenuItem* item )
{
    return ScrollMenu::create(item, nullptr);
}
 
ScrollMenu* ScrollMenu::createWithItems( MenuItem *item, va_list args )
{
    Vector<MenuItem*> items;
    if( item ) {
        items.pushBack(item);
        MenuItem *= va_arg(args, MenuItem*);
        while(i) {
            items.pushBack(i);
            i = va_arg(args, MenuItem*);
        }
    }
    return ScrollMenu::createWithArray(items);
}
 
ScrollMenu* ScrollMenu::create( MenuItem* item, ... )
{
    va_list args;
    va_start(args,item);
    
    ScrollMenu *ret = ScrollMenu::createWithItems(item, args);
    
    va_end(args);
    
    return ret;
}
 
bool ScrollMenu::onTouchBegan(Touch* touch, Event* event)
{
    m_touchesStart = touch->getStartLocation();
    
    return Menu::onTouchBegan(touch, event);
    
}
 
void ScrollMenu::onTouchMoved(Touch* touch, Event* event)
{
    Menu::onTouchMoved(touch, event);
    
}
 
void ScrollMenu::onTouchEnded(Touch* touch, Event *event)
{
    // move은 가로 or 세로로 얼마만큼 Move되었나를 체크하여 그 이상이면 버튼 동작을 해지 합니다.
    // 값이 낮을수록 민감하게 반응하지만 너무 낮으면 버튼 터치가 힘들 수 있습니다.
    float move = 30.f;
    
    m_touchesEnd = touch->getLocation();
    Vec2 difference = m_touchesEnd - m_touchesStart;
    
    if (difference.x > move || difference.y > move ) {
        Menu::onTouchCancelled(touch, event);
    }
    else if (difference.x < -move || difference.y < -move) {
        Menu::onTouchCancelled(touch, event);
    }
    else {
        Menu::onTouchEnded(touch, event);
    }
}
 
 
cs

다른 부분은 수정할 부분이 없는데 float move = 30.f 이부분만 필요에 따라 수정하시면 됩니다.

값을 수정해 가며 스타일에 따라 테스트 해보시면 편합니다.



[ 사용 예제 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
bool HelloWorld::init()
{
    if ( !Layer::init() ) return false;
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
 
    // ScrollView 생성
    ScrollView* scrollView = ScrollView::create(Size(visibleSize.width, visibleSize.height));
    scrollView->setDirection(ScrollView::Direction::HORIZONTAL);
    scrollView->setContentOffset(Vec2(00));
    scrollView->setContentOffsetInDuration(Vec2(00), 10.f);
    scrollView->setPosition(visibleSize * 0.5f);
    this->addChild(scrollView);
 
    // Menu 생성
    auto closeItem = MenuItemImage::create("CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
    closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));
 
    //auto menu = Menu::create(closeItem, NULL);
    // 기존에 Menu를 ScrollMenu로 변경만 하면 됩니다.
    auto menu = ScrollMenu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);
    
    // ScrollView에 버튼 붙이기
    scrollView->addChild(menu);
 
    return true;
}
cs

ListView, TableView에서도 사용 가능 합니다.


문제있으면 알려주세요 ^^

2.x 버전도 곧 올리겠습니다.


(추가적으로 ui::Widget에 있는 ScrollView와 Button을 이용한 방법도 올리겠습니다.)

반응형
반응형
이미 모두 알고 계시거나 간단한 건데 어떤분이 질문을 해서 글 남깁니다.

ScaleTo Action class 를 가지고 액션처리를 하는데 
ScaleXTo 나 ScaleYTo class 는 왜 없냐 라고 질문을 주셨습니다.

그런 class는 없지만 똑같은 기능을 하는 함수는 있습니다.

액션을 생성할 때 여러가지 방법이 있습니다.
방법은 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
    // 동시에 X 와 Y 를 변경하는 생성 방법.
    auto actionScale = ScaleTo::create(1.0f, 2.0f);
    
    // X 만 변경하는 생성 방법
    auto actionScaleX = ScaleTo::create(1.0f, 2.0f, 1.0f);
    
    // Y 만 변경하는 생성 방법
    auto actionScaleY = ScaleTo::create(1.0f, 1.0f, 2.0f);
    
    // X 가 먼저 변하고 Y 가 변경되는 방법
    auto actionSequence = Sequence::create(actionScaleX,
                                           actionScaleY,
                                           NULL);
 
cs


ScaleTo를 생성할 때 X 값과 Y 값을 따로 지정이 가능 합니다.

Z 값도 변경 가능한데 아직 써보지는 못했내요 ^^

cocos2d-x가 찾아보면 편리한 기능이 엄청 많은 것 같습니다.
한번쯤 엔진 소스를 구경해 보는 것도 개발에 많은 도움이 됩니다.

 


반응형
반응형

Label을 쓰다보면 같은 크기내에 텍스트를 더 많이 노출 시켜야 할 때도 있습니다.

Label의 크기를 지정하면 자동 줄바꿈이 되기도 하지만 게임에서 일정 부분에만 노출되어야 하는 텍스트들도 존재합니다.

그래서 Label의 폰트 크기를 50으로 사용했다가 텍스트가 Label의 크기를 넘어갈 때는 폰트 사이즈를 40으로 변경해야 합니다.


Label안에 당연히 setFontSize 같은 함수가 있을 줄 알았는데...

아무리 찾아봐도 안보이내요. ㅠ_ㅠ


cocos2d-x 2.x 버전에서는 setFontSize(float size) 함수가 있어서 편했는데 

cocos2d-x 3.x 와서 없어진듯 ㅠㅠ


조금은 불편하지만 방법은 있습니다.


<Label 폰트 사이즈 변경 방법>

1
2
3
4
5
6
7
8
9
10
11
12
// 폰트 크기 48 Label 생성.    
auto label = Label::createWithTTF("Test Font Size""NanumGothic.ttf"48);     
   
// TTF폰트 정보 가져오기.    
TTFConfig config = m_labelNumber->getTTFConfig();   
     
// 원하는 폰트 사이즈로 변경.    
config.fontSize = 38;       
 
// 변경한 폰트 정보를 다시 입력.    
label->setTTFConfig(config);
 



cs


폰트 크기를 정해놓고 줄이는 방법도 있지만

크기에 맞게 폰트사이즈를 조절 할 수 있다면 얼마나 좋을까요?

아직 cocos2d-x에는 기능이 없는것 같습니다.

없으면 만들면 돼죠^^;;


그래서 간단하게 자동 폰트 사이즈  변경하는 방법을 만들어 봤습니다.

그렇게 효율적인 방법은 아니지만 간단하게 사용 가능 합니다.

(더 효율적인 방법이 있으면 알려주세요 ㅠ_ㅠ)


<Label 폰트 사이즈 자동 조절>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
    // label을 생성.
    auto label = Label::createWithTTF("Test Font Size""NanumGothic.ttf"48);
 
    // TTF폰트 정보 가져오기.
    TTFConfig config = m_labelNumber->getTTFConfig();
 
    // label의 Width가 96보다 작을 때까지 반복.
    while (label->getContentSize().width > 96.f)
    {
        // 현재 label의 크기가 96보다 크다면 폰트사이즈를 1 줄인다.
        config.fontSize = --config.fontSize;
        
        // 폰트 사이즈가 1보다 작으면 끝냄.
        if (config.fontSize < 1break;
        
        // 폰트 사이즈를 줄이고 TTF설정을 저장.
        label->setTTFConfig(config);
    }
cs



위 처럼 해주면 label의 Width 크기가 96보다 작을 때까지 폰트사이즈를 줄입니다.

너무 많은 텍스트를 넣으면 1보다 작아져 안되겠지만...


숫자 2~4자리 정도 가변적으로 폰트사이즈를 조절해야할 경우 유용하게 사용할 수 있습니다.




반응형
반응형

항상 최신 버전을 사용하고 싶은 욕심에 3.6버전에서 3.8.1을 파일을 cocos2d 폴더만 복사하여 덮어 씌우기를 했다.

기분 좋게 컴파일을 했는데 아래와 같은 에러가 발생했다.

Undefined symbols for architecture x86_64:

“_iconv“, referenced from:

cocos2d::FontAtlas::conversionU16TOGB2312(std::__1::basic_string<char16_t, std::__1::char_traits, std::__1::allocator > const&, std::__1::unordered_map, std::__1::equal_to, std::__1::allocator<std::__1::pair > >&) in libcocos2d iOS.a(CCFontAtlas.o)

“_iconv_close“, referenced from:

cocos2d::FontAtlas::~FontAtlas() in libcocos2d iOS.a(CCFontAtlas.o)

“_iconv_open“, referenced from:

cocos2d::FontAtlas::conversionU16TOGB2312(std::__1::basic_string<char16_t, std::__1::char_traits, std::__1::allocator > const&, std::__1::unordered_map, std::__1::equal_to, std::__1::allocator<std::__1::pair > >&) in libcocos2d iOS.a(CCFontAtlas.o)

ld: symbol(s) not found for architecture x86_64

clang: error: linker command failed with exit code 1 (use -v to see invocation)


원인은 FontAtlas에서 libiconv.dlib를 사용해서 문제였다.

3.6에서는 사용하지 않았는데 어떤 이유인지는 모르지만 3.8.1부터는 libiconv.dlib를 사용하기 때문에 라이브러리에 추가를 해줘야 한다.


[방법] 

프로젝트 → 「General」→ 「Linked Frameworks and Libraries "에"libiconv.dlib "을 추가 또는 "libiconv.tbd"를 추가하면 문제 해결





처음부터 3.8.1올 프로젝트를 생성하거나 라이브러리가 이미 추가되어 있으면 문제 없겠지만 

cocos2d폴더만 덮어 씌우기를 했기때문에 발생한 문제이다.



반응형
반응형

프로젝트를 새로 생성하자마자 컴파일을 하면 아래와 같은 에러가 발생한다.


ld: in /Users/rudalstb/Library/Developer/Xcode/DerivedData/TestGame-hfnevdaxcjkgjodopextjiscsyhu/Build/Products/Debug-iphonesimulator/libcocos2d iOS.a(ftbase.ios8_x86_64.o), building for iOS simulator, but linking in object file built for OSX, for architecture x86_64


clang: error: linker command failed with exit code 1 (use -v to see invocation)


아무것도 안했는데 에러 발생!


cocos2d-x 포럼에서 뒤져 보니깐 3.8 버전에서 문제가 있는 것 같다.


참고: http://discuss.cocos2d-x.org/t/cocos2d-x-v3-8-rc0-ios-simulator-linking-error/23635


간단하게 수정 가능하다.


"프로젝트"/cocos2d/external/freetype2/prebuilt/ios/libfreetype.a"

위 폴더로 이동하여 libfreetype.a 파일을 cocos2d-x 3.7 버전에 파일로 교체해주면 완료.


위와 같이 수정시 매번 프로젝트 생성시마다 교체해줘야 하므로 아예 cocos2-x 원본 파일을 교체해주면 

한번 교체로 쭈~~~~~~~욱 쓸수 있다.


cocos2d-x 3.9 버전이 곧 출시되니 지금은 불편하더라도 참자!


[ 2015-11-3 추가 내용 ]

cocos2d-x 3.8.1 버전이 나왔내요~ 

이버전에서는 문제가 해결되었습니다. 3.9 나오기전까지 3.8.1 쓰면될 듯 합니다.

반응형
반응형

cocos2d-x로 게임을 만들수록 새로운 기능들을 알게 되는데 

이번에 새로 알게된 좋은 기능이 있어 소개합니다.

(이미 아는 분들도 있겠지만 ^^ 혹시 저처럼 몰라서 힘들게 돌아가는 길을 택한 분들께 바칩니다.)


애니메이션을 구현하다 보면 노드에 액션을 주고 액션이 끝났을 경우 그 노드를 삭제해야 하는 경우가 있습니다.

어제 까지만 해도 항상 시퀀스를 사용해서 마지막 시퀀스 파라메터에 콜백함수를 주고 노드를 넘겨 삭제를 했었습니다.

(참 바보 같죠 ㅠ_ㅠ)


예를 들어 아래와 같이 구현 했었죠.


void SceneGame::animateBlockMoveDown() // 애니메이션 구현

{

    auto node = Node::create();

    auto actionMove = MoveTo::create(0.3f, Vec2(100, 100));

    auto callbackFunc = CallFuncN::create(CC_CALLBACK_1(SceneGame::callbackBlockMoveDownEnd, this));

   

    auto actionSeq = Sequence::create( actionMove, callbackFuncNULL);


    node->runAction(actionSeq);

}


void SceneGame::callbackBlockMoveDownEnd(cocos2d::Ref *ref)  // 노드 삭제 처리

{

    auto node = dynamic_cast<Node*>(ref);

    node->removeFromParentAndCleanup(true);

}


 문제가 있는 코드는 아닙니다.


콜백함수에서 애니메이션이 끝난 뒤 다른 처리를 하지 않고 노드삭제 처리만 할 거라면 

콜백함수를 만들어야 하는 귀차니즘과 모든 액션에 콜백을 만들어야 한다면 소스코드 또한 지저분해질수 있습니다.

(콜백 함수만... 20개...모두 액션 삭제처리만...물론 노드 삭제처리 콜백 함수 하나만 만들어도 됩니다. ^^)


좀더 깔끔하고 멋진 코드를 위해 사용 하시면 좋을 것 같습니다.


RemoveSelf 액션 함수 사용법에 대해 알아 볼께요.

void SceneGame::animateBlockMoveDown() // 애니메이션 구현 및 노드 삭제 처리

{

    auto node = Node::create();

    auto actionMove = MoveTo::create(0.3f, Vec2(100, 100));

    auto actionRemove = RemoveSelf::create();

    

    auto actionSeq = Sequence::create( actionMove, actionRemove, NULL);


    node->runAction(actionSeq);

}

위 대로 하면 애니메이션 구현 및 노드 삭제처리를 한번에 할 수 있습니다.



update 함수를 보면 아래와 같이 구현 되어 있습니다.

void RemoveSelf::update(float time) 

{

    CC_UNUSED_PARAM(time);

    _target->removeFromParentAndCleanup(_isNeedCleanUp);

}

removeFromParentAndCleanup기능을 하는 액션인거죠. ^^



파라메터로 cleanup 여부를 전달 할 수 있는데 Default가 true로 되어 있습니다.

static RemoveSelf * create(bool isNeedCleanUp = true);



이편한걸 이제야 알았다는게 조금 힘드내요. ^^

사용한지 아직 하루 밖에 안되어서 무슨 문제가 있는지 모르겠지만

문제점을 알고 계신 분은 코멘트 남겨주세요.


감사합니다.

반응형
반응형

2달전 cocos2d-x 사이트에서 sdkbox 라는걸 보고 흥미를 가지게 되었다.

유니티 애셋 스토어에서 많이들 팔고 있는 통합 플러그인을 cocos에서 

cocos2d-x용으로 만들고 있는 것이 였다.


cocos2d-x에서도 Plugin-X라는 것이 있긴 했지만 사용해 보지는 않았다.

(사용해보려고 했지만 그렇게 간편하지도 않고 Native로 개발하는 것과 큰차이 없어 보였음)


그런데 이번에 나온 sdkbox는 큰 매력이다. 

정말 단한줄로 iOS/Android 모두 사용 가능하다는 것과 기존에 콜백함수를 

각OS별로 따로 만들어 여러가지 방법으로(노티피케이션 등) Scene으로 

되돌려주는 코드도 만들어야 했다.


그런데 이번에 나온 sdkbox는 리스너까지 개발 되어 리스너 등록만 해주면 현재 Scene으로 

알아서 콜백 함수를 호출해 준다.


정말로 Android/iOS IAP가 통합된 기분 이랄까? 하루종일 걸릴꺼 1시간이면 하는 기분?


그 외에도 페이스북/방글/애널리틱스/애드콜로니 등등 여러가지 플러그인을 사용 가능하며

직접 문의한 결과 애드몹도 지원 예정에 있다고 한다.


점점 코코스에서 유니티를 따라잡기 위해 편의를 제공하는 것 같다.

현재 IAP와 애널리틱스, 페이스북까지 테스트를 완료 해봤으며 잘된다.

IAP에서 약간의 문제가 있었지만 지속적인 코코스 애들과 (짧디 짧은 영어로) 커뮤니케이션을

해서 많은 오류를 해결 했다.(첨엔 안드로이드 소모/비소모 아이템이 안되었음 ㅠㅠ)


시간 되는 대로 각 sdkbox에 사용 방법과 쉽게 사용하는 방법에 대해 포스팅하려고 한다.

(현재 한국에서 sdkbox를 아직 다루는 분이 없는것 같다 ㅠ ㅠ )


cocos2d-x sdkbox site

http://www.cocos2d-x.org/sdkbox

반응형
반응형

Game Scene에 터치 리스너 붙이고 게임을 하다 Pause나 Shop같은 Layer를 띄웠을 경우 

Game Scene에 터치가 계속 먹어서 애를 먹은 적이 있다.


그래서 모달 레이어를 만드는 함수를 만들어 봤다.


cocos2d-x function


// 레이어(노드)를 모달로 만들어 주는 함수

void GameScene::modal_layer(cocos2d::Node *node)

{

    auto listener = EventListenerTouchOneByOne::create();

    listener->setSwallowTouches(true);

    listener->onTouchBegan = [](Touch *touch,Event*event)->bool {

        return true;

    };

    

    auto dispatcher = Director::getInstance()->getEventDispatcher();

    dispatcher->addEventListenerWithSceneGraphPriority(listener, node);

}


사용 예


auto modalLayer = Layer::create();

modal_layer( modalLayer ); 

this->addchild( modalLeyer );


Modal Layer 해제


this->removeChildmodalLayer );



GameScene에 멤버 함수로 넣어도 되고 모든 Scene에서 사용하겠다고 하면 static으로 만들던 Global함수로 만들던 상관 없다.

Layer를 remove시키면 Modal Layer 자동으로 해제 된다.

모달 레이어를 간단하게 사용할 수 있는 방법이다.


반응형

+ Recent posts