ぎじゅつめもブログ

主にアプリ開発の技術メモを残していきます。

【cocos2d-x】Spineスケルタルアニメーションを左右反転

Spineで作成したスケルタルアニメーションを左右反転する方法です。

(環境:cocos2d-x-3.2, spine 1.9.15)

ランタイムのドキュメントにある Skeleton > flipX,flipY というフィールドが左右反転に使えます。

SkeletonAnimation* animation = SkeletonAnimation::createWithFile("spine.json", "spine.atlas", 0.20f);

/*
以下のようにして向きを変更できます
反転させるには1.0を指定。元のままの場合は0.0。
*/
float flip = 1.0f;
animation->skeleton->flipX = flip;

animation->setAnimation(1, "idle", true);

以上です。

※参考:
Spine cocos2d-xランタイムドキュメント:http://ja.esotericsoftware.com/spine-using-runtimes


関係ないですけど、spineのatlasファイルの「size: 〜,〜」っていう行があると、spineアニメーション生成時にエラーが発生します。
この行を消すとエラーは解消されますが、他に対処法はないのでしょうか。。

【Objective-C】UICollectionViewでのページスクロール

UICollectionViewをページスクロールにしたいときのメモです。
(環境:xcode5.1.1, iOS 7.1)

pagingEnabled = YES とすればできますが、各セルの間に間隔をいれたいときは、これだと表示位置がずれていってしまいます。
そこで、UICollectionViewFlowLayout (UICollectionViewのレイアウトをカスタマイズできるクラス) を継承したクラスを作成し、セルが画面の中央に常にくるように処理を書きます。

targetContentOffsetForProposedContentOffset::メソッドをオーバーライドします。
下記の例は、1列のシンプルなコレクションビューで、セルの真ん中(x軸)が画面の真ん中にくるように設定しています。

/*HogeCollectionViewFlowLayout.m*/

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    CGFloat offsetAdjustment = MAXFLOAT;
    CGFloat horizontalCenter = proposedContentOffset.x + (CGRectGetWidth(self.collectionView.bounds) / 2.0);
    
    CGRect targetRect = CGRectMake(proposedContentOffset.x, 
                                                        0.0, 
                                                        self.collectionView.bounds.size.width, 
                                                        self.collectionView.bounds.size.height);
    
    NSArray* array = [self layoutAttributesForElementsInRect:targetRect];
    
    for (UICollectionViewLayoutAttributes* layoutAttributes in array) {
        if (layoutAttributes.representedElementCategory != UICollectionElementCategoryCell)
            continue;
    
        CGFloat itemHorizontalCenter = layoutAttributes.center.x;
        offsetAdjustment = itemHorizontalCenter - horizontalCenter;
        layoutAttributes.alpha = 0;
        
        if (velocity.x < 0) break;
    }
    
    return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}

また、ページングの動作に近くするため、UICollectionViewクラスでスクロールの速度を速くします。

/*HogeCollectionViewController.m*/
//viewDidLoadなどで...
self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast;

以上です。

※参考
http://stackoverflow.com/questions/13492037/targetcontentoffsetforproposedcontentoffsetwithscrollingvelocity-without-subcla

【Android】画像をローディング(ぐるぐる)にする

ローディングを使う際、AndroidではProgressBarのスタイルを progressBarStyle に設定することでデフォルトのぐるぐる部品が表示されます。そのぐるぐる画像を変えたいときのメモです。

(環境:android 4.2, 確認端末:F-03F)

こちらのサイトをそのまま使えばOKでした。

まず、custom_progress_background.xml (名前は何でもいいです)を用意し、drawableフォルダに格納します。
上記サイトの例では2つの画像を用意してそれらが逆向きに回るローディングを作成しています。

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <rotate
            android:drawable="@drawable/spinner_outer"
            android:pivotX="50%"
            android:pivotY="50%"
            android:fromDegrees="0"
            android:toDegrees="1080" />
    </item>
    <item>
        <rotate
            android:drawable="@drawable/spinner_inner"
            android:pivotX="50%"
            android:pivotY="50%"
            android:fromDegrees="720"
            android:toDegrees="0" />
    </item>
</layer-list>

画像をdrawableフォルダにいれます。

  • spinner_inner.png
  • spinner_outer.png


あとは呼び出したいアクティビティで作成したプログレスバーを指定します。

<ProgressBar
    android:id="@+id/custom_progress"
    android:indeterminate="true"
    style="?android:attr/progressBarStyleLarge"
    android:layout_width="210dip"
    android:layout_height="210dip"
    android:layout_gravity="center"
    android:indeterminateDrawable="@drawable/custom_progress_background"
    /> 
  • イメージ

f:id:tsuyushiga:20140607145242p:plain


以上です。


※参考
http://pankajchunchun.wordpress.com/2011/09/10/customization-of-spinner-progress/

【cocos2d-x】picojsonでJSONファイルを読み込む

cocos2d-x 3.x になってから2.x系から大幅に変更があって戸惑いました。(クラス名の頭からCCがとれたからそう感じるだけでしょうか。)

ローカルのjsonを読み込むメモです。cocos2d-xプロジェクトへ追加したjsonを使用します。
picojsonというjsonをパースしてくれる便利なライブラリを使います。
(環境:xcode5.1, cocos2d-x-3.1rc0)

{
    "hoge" :[
                {"x":1, "y":1, "z":1}, 
                {"x":2, "y":1, "z":1}
                ]
}
//ファイルパスの取得
FileUtils* fileUtils = FileUtils::getInstance();
const char* path = "hogehoge.json";
std::string fullpath = fileUtils->fullPathForFilename(path);

// ファイルオープン
ifstream inputStream;
string thisLine;
inputStream.open(fullpath.c_str());
if (!inputStream.is_open())
{
    cerr << "cannot open file!" << endl;
    exit(1);
}

stringstream sstream;
while (getline(inputStream, thisLine))
{
    sstream << thisLine;
}
inputStream.close();
cout << "finish opening file!" << endl;
    
CCLOG("sstream:%s", sstream.str().c_str());
    
// JSONのパース
picojson::value v; 
picojson::parse(v, sstream);

picojson::object& all = v.get<picojson::object>();
picojson::array& array = all["hoge"].get<picojson::array>();
for (picojson::array::iterator it = array.begin(); it != array.end(); it++)
{
    picojson::object& tmpObject = it->get<picojson::object>();
    int x = (int)tmpObject["x"].get<double>();
    int y = (int)tmpObject["y"].get<double>();
    int z = (int)tmpObject["z"].get<double>();
    CCLOG("x:%d, y:%d, z:%d", x, y, z);
}

以上です。

参考
http://taichino.com/programming/2068
http://nirasan.hatenablog.com/entry/2013/10/24/232905
http://stackoverflow.com/questions/12171445/file-i-o-using-cocos2d-x

【cocos2d-x】CCScrollViewでページスクロール

スクロールビューを使って、ページ切り替えをしたいときのメモです。
(環境:xcode5.1, cocos2d-x 2.2.2)

iPhone開発ではUIScrollViewにpagingEnabledというプロパティがあり、これをオンにするだけで実装可能ですが、cocos2d-xでは同じようなプロパティはないみたいです。

なのでCCScrollViewを少しいじってページ切り替えを出来るようにします。

まずヘッダーを編集します。以下の宣言を追加します。

/**
 * CCScrollView.h
 */
//...略
public:
    bool isPagingEnabled() { return _isPagingEnabled; }
    void setEnablePaging(bool isPagingEnabled) { _isPagingEnabled = isPagingEnabled; }
    void setPageSize(CCSize _pageSize){ pageSize = _pageSize; }
    CCSize getPageSize(){ return pageSize; }
protected:
    bool _isPagingEnabled;
    CCSize pageSize;
//略...

次にrelocateContatiner(bool animated)というメソッドに以下のように追記します。

/**
 * CCScrollView.cpp
 */
void CCScrollView::relocateContainer(bool animated)
{
//...略

    newX     = oldPoint.x;
    newY     = oldPoint.y;
    
    // ここから
    {
        if (_isPagingEnabled) {
            int horizontalPageNum = roundf(newX / pageSize.width);
            int verticalPageNum = roundf(newY / pageSize.height);
            
            if (m_eDirection == kCCScrollViewDirectionBoth || m_eDirection == kCCScrollViewDirectionHorizontal){
                newX = horizontalPageNum * pageSize.width;
            }
            
            if (m_eDirection == kCCScrollViewDirectionBoth || m_eDirection == kCCScrollViewDirectionVertical){
                newY = verticalPageNum * pageSize.height;
            }
        }
    }
    // ここまで追記
    
    if (m_eDirection == kCCScrollViewDirectionBoth || m_eDirection == kCCScrollViewDirectionHorizontal)
    {
        newX     = MAX(newX, min.x);
        newX     = MIN(newX, max.x);
    }
//略...
}

実際にスクロールビューを使うときには以下のようにして、ページ切替機能をオンにしてあげます。

    CCScrollView *scrollView = CCScrollView::create(CCSize(visibleSize.width, visibleSize.height));
    // スクロールビューをお好みで設定...
    scrollView->setEnablePaging(true);

以上です。

※参考
http://blackxxxwhite.blogspot.jp/2013/07/cocos2d-x-ccscrollview-semi-paging.html




  • 2014/8/9追記

cocos2d-x-3.x以降の場合は下記のようにします。

/**
 * CCScrollView.h
 */
//...略
public:
    bool isPagingEnabled() { return _isPagingEnabled; }
    void setEnablePaging(bool isPagingEnabled) { _isPagingEnabled = isPagingEnabled; }
    void setPageSize(Size _pageSize){ pageSize = _pageSize; }
    Size getPageSize(){ return pageSize; }
protected:
    bool _isPagingEnabled;
    Size pageSize;
//...略
/**
 * CCScrollView.cpp
 */
void CCScrollView::relocateContainer(bool animated)
{
    //...略

    newX     = oldPoint.x;
    newY     = oldPoint.y;

    // ここから
    {
        if (_isPagingEnabled) {
            int horizontalPageNum = roundf(newX / pageSize.width);
            int verticalPageNum = roundf(newY / pageSize.height);
            
            if (_direction == Direction::BOTH || _direction == Direction::HORIZONTAL){
                newX = horizontalPageNum * pageSize.width;
            }
            
            if (_direction == Direction::BOTH || _direction == Direction::VERTICAL){
                newY = verticalPageNum * pageSize.height;
            }
        }
    }
    // ここまで追記
    
    if (_direction == Direction::BOTH || _direction == Direction::HORIZONTAL)
    {
        newX     = MAX(newX, min.x);
        newX     = MIN(newX, max.x);
    }

    //略...
}

【Android】ExpandableListViewの文字色を変える

これからはAndroidの開発メモも残していきます(次の案件がAndroidなので。。)

AndroidSDK付属のレイアウト(android.R.layout.simple_expandable_list_item_1, android.R.layout.simple_expandable_list_item_2)を使ってExpandableListViewをつくる際に、文字色を変える方法です。
(環境:Android4.2.2)

SimpleExpandableListAdapterをオーバーライドします。

※ExpandableListViewの基本的な使い方は、ここを参考にしました。

SimpleExpandableListAdapter adapter = new SimpleExpandableListAdapter(
    getApplicationContext(),
    groupData,
    android.R.layout.simple_expandable_list_item_1,
    new String[] {"group"},
    new int[] {android.R.id.text1},
    childData,
    android.R.layout.simple_expandable_list_item_2,
    new String[] {"name", "group"},
    new int[] {android.R.id.text1, android.R.id.text2}) {
        @Override
	public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
	    final View itemRenderer = super.getChildView(groupPosition, childPosition, isLastChild, convertView, parent);
            final TextView tv1 = (TextView)itemRenderer.findViewById(android.R.id.text1);
            final TextView tv2 = (TextView)itemRenderer.findViewById(android.R.id.text2);
            tv1.setTextColor(0xff000000); // 子リストのタイトルは黒に設定
            tv2.setTextColor(0xff0000ff); // 子リストのサブタイトルは青に設定
            return itemRenderer;
        }
	@Override
	public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
	    final View itemRenderer = super.getGroupView(groupPosition, isExpanded, convertView, parent);
            final TextView tv1 = (TextView)itemRenderer.findViewById(android.R.id.text1);
            tv1.setTextColor(0xff0000ff); // 親リストのタイトルは青に設定
            return itemRenderer;				
	}
};
		
ExpandableListView listView = (ExpandableListView)findViewById(R.id.hogelistView);
listView.setAdapter(adapter);

以上です。

※参考
http://stackoverflow.com/questions/16588659/android-changing-color-of-textview-in-simpleexpandablelistadapter

【Objective-C】SDWebImageで画像サイズを変える

SDWebImageはFacebookアプリにも使われていると言われている、ものすご便利〜なライブラリです。

  • SDWebImage

 https://github.com/rs/SDWebImage

サーバーからイメージを取得する前に、アプリ側で画像サイズ変えられないですか?みたいな事態が生じたので、めんどくさいな〜と思いつつ、方法を探してみました。デリゲートメソッドを使用する方法です。

まずヘッダファイルではSDWebImageManagerDelegateプロトコルを採用します。

// HogeFugaViewController.h
@interface HogeFugaViewController : UITableViewController <SDWebImageManagerDelegate>

どこかでデリゲートを設定します。

// HogeFugaViewController.m
// webイメージ読み込みデリゲート設定
SDWebImageManager.sharedManager.delegate = self;

デリゲートメソッドを実装します。

// HogeFugaViewController.m
- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL
{
    /*イメージをリサイズする*/
    CGSize resizedImageSize = CGSizeMake(50, 50);
    UIGraphicsBeginImageContextWithOptions(resizedImageSize, NO, 0.0);
    [image drawInRect:CGRectMake(0, 0, resizedImageSize.width, resizedImageSize.height)];
    UIImage* resizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    image = resizedImage;
    return image;
}

上記のように実装するとSDWebImageライブラリを使って画像を設定する際、画像がキャッシュに保存される前にイメージをリサイズしてくれます。

NSURL* url = /*イメージのURL*/;
UIImage* placeholder = /*プレースホルダイメージ*/;
[cell.hogeImage setImageWithURL:url placeholderImage:placeholder];

しかし、上記の対応だけではSDWebImageでキャッシュするイメージがスケール1.0で保存されてしまっていました。。
SDWebImageライブラリだとURLに @2x という名前がないとUIImageのスケールが 1.0 で保存されるっぽいです。(ちゃんとは確かめてません)
スクリーンのスケールからRetinaディスプレイかどうか判断して,Retinaディスプレイの場合はUIImageのスケールを2倍に設定し直すようにライブラリを修正しました。(微妙な対応ですね。。もっといい方法はないでしょうか)

// UIImageView+WebCache.m
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock {
/*略*/
            if (!wself) return;
            dispatch_main_sync_safe(^{
                if (!wself) return;
                if (image) {       //ここから
                    if ([UIScreen mainScreen].scale == 2.0)
                    {
                        wself.image = [UIImage imageWithCGImage:image.CGImage scale:2.0f orientation:UIImageOrientationUp];
                    }
                    else
                    {                  //ここまで追記
                        wself.image = image;
                    }
                    [wself setNeedsLayout];
                }
/*略*/
}

以上です。



※補足

上記の例ではイメージのサイズを幅50、高さ50で固定でしたので、以下のライブラリを使えば出来そうです。
https://github.com/toptierlabs/ImageCacheResize

ただし、動的にサイズを変えたいときはデリゲートを使うのが良さそうです。

■参考ページ
https://github.com/rs/SDWebImage
http://hackemist.com/SDWebImage/doc/Protocols/SDWebImageManagerDelegate.html#//api/name/imageManager:transformDownloadedImage:withURL:
http://stackoverflow.com/questions/12928103/sdwebimage-process-images-before-caching