Xcode8.3でxvimを使う

この記事は

https://github.com/XVimProject/XVim

xvimとはxcodevimが使えるようにするプラグインのことです。

今回はこれをxcode8.3に入れます。

xcodeをバージョンアップしたらxvimが使えなくなってしまったので、それの解消方法をまとめときます。

この記事は以下の記事から今回自分が必要だった部分だけ抜粋させていただきました。感謝。

www.ie-kau.net

qiita.com

試した環境

macOS sierra  Xcode8.3.2(8E2002)

本題

まず古いのが残っている場合は消します。

cd ~/XVim  // Xvimを置いてるディレクトリへ移動。このディレクトリは適宜変更してください。
make uninstall
rm -r ~/XVim

証明書を作って、それを登録します。

https://github.com/XVimProject/XVim/blob/master/INSTALL_Xcode8.md

証明書を作る手順は上の通り。

ここでは上の手順の通りに XcodeSigner という名前で作ったので、それを登録します。違う名前で作った人は XcodeSignerの部分を変えてください。

sudo codesign -f -s XcodeSigner /Applications/Xcode.app  

keith さんのコードを使う

xcodeを8.3にした時点で何度かインストールをしてみたのですが、make自体が通らなくなってしまいました。

issueに上がっていたみたいですでに対応してくれた人がいるらしいのでそちらのソースでコンパイルすることにします。

github.com

git clone https://github.com/keith/XVim.git
git fetch origin xcode-8.3-release
git checkout xcode-8.3-release
make

これでmakeが通るようになりました。

UUIDを登録する

github.com

手動でやってもいいんですが、一括でやってくれる上のツールをインストールします。

gem install update_xcode_plugins
update_xcode_plugins

自分の場合、これでうまく行かなかったので以下をしたところ、無事xvimが有効になりました。

update_xcode_plugins --unsign

Execution failed for task ':android:compileReleaseJavaWithJavac'. compileSdkVersion 'android-24' requires JDK 1.8 or later to compile.

apkファイル作ろうとした際に以下のエラー発生。

Execution failed for task ':android:compileReleaseJavaWithJavac'.compileSdkVersion 'android-24' requires JDK 1.8 or later to compile.

この直前に targetSdkVersionを24にあげてました。

エラーメッセージにある通り、API24 から?(おそらく。23では大丈夫だったため) JDK1.8 が必要になったらしいです。

javaのバージョン確認

インストールしてあるjdkのバージョンを確認。

java -version

または

javac -version

知らなかったけど、コンパイラのバージョンを見るときは, java じゃなくて javac でみたほうがいいらしい。

jdk7をアンインストール → これでとりあえず解決したよーー

確認した結果versionは1.8でした。なのにこのエラーが出る。

javaが複数いるっぽい気がしたので、/Library/Java/JavaVirtualMachinesディレクトリを見てみると、jdk7と8がいる。#Mac OSX Yosemite でやってました。

なんとなくjdk7のほうが使われているようなので、jdk7をアンインストールしてみます。

JDK 7 Mac Install

oracleの↑のページの通りにやればいい。ディレクトリごと消すだけ。権限がないときは、sudo は必要でした。

rm -rf jdk1.7.0_06.jdk

これはjdk7の手順みたいです。jdk6以前は違うみたいなので注意。

これでとりあえずいくようになりました。

直接android studio 使っている人とかは、android studio の設定で使うjdk決めれたりするみたいだし、 jdkのバージョン複数持ってないといけない人もいると思うので、古いバージョンは削除してもOK! な環境な人しかダメなんで、とりあえずってかんじです…

libgdxでscaleするの起点位置の指定の仕方

ふと、scaleする起点?中心?の位置が画像の左下とか真ん中とかしかやったことなくって、

= Actor#setOrigin (int alignment)

画像内の好きな位置にしたかったんだけど、わかんなかったからしょぼいけどメモ。

public class MyGdxGame extends ApplicationAdapter {
    SpriteBatch batch;
    Image img;
    Stage stage;
    
    @Override
    public void create () {
        stage = new Stage(  );
        batch = new SpriteBatch();

        img = new Image(new Texture("badlogic.jpg"));
        stage.addActor(img);
        //img.setOrigin( Align.center ); //真ん中
        //img.setOrigin( img.getWidth()/2, img.getHeight()/2 ); //これも真ん中
        img.setOrigin( img.getWidth() / 2, 0 );  //
        img.addAction( Actions.scaleTo(0.0f, 0.0f, 2f) );

    }

    @Override
    public void render () {
        Gdx.gl.glClearColor( 1, 0, 0, 1 );
        Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT );
        batch.begin();
        stage.draw();
        batch.end();
        stage.act( Gdx.graphics.getDeltaTime() );
    }
    
    @Override
    public void dispose () {
        batch.dispose();
    }

cocos 2dx メモ(整理するまでcppのないようも含んでると思う

xcodeで作ったクラスをandroid用にビルドする時には、Android.mkにクラス名を追加

HelloCocos/proj.android-studio/Android.mk

android-studioでビルドしているのでこのディレクトリは以下のAndroid.mkを編集する

LOCAL_SRC_FILESにcpp名を追加する

LOCAL_SRC_FILES := hellocpp/main.cpp \
                   ../../../Classes/AppDelegate.cpp \
                   ../../../Classes/HelloWorldScene.cpp \
                   ../../../Classes/LogoScene.cpp \
                   ../../../Classes/MainMenuScene.cpp \
                   ../../../Classes/HighScoreScene.cpp

android用にビルド

cocos2d-x-3.10//tools/cocos2d-console/bin/cocos run -s ~/snk_cocos/snk -p android --android-studio

毎フレ呼ばれるupdateメソッドを使う

事前にinitとかで以下のcocos2d::Node#scheduleUpdateを呼んでおく必要がある

this->scheduleUpdate();

updateメソッドがマイフレ使える

void update(float dt);

リファレンスカウンタ

snoopopo.hatenablog.com

インクルードガード、宣言と定義

・メンバがポインタの場合は、宣言でいい。定義はいらない=ヘッダをincludeしてクラスの定義を読み込む必要がない。

・引数、戻り値も宣言でいい。

・メンバが実態の場合は、定義を持つ必要がある。

・意識せずにヘッダを2回includeしててclassが二重定義になってエラった → インクルードガードすればOK。

⇒基本的に全部のヘッダはインクルードガードする!

#ifndefマクロは、xxが定義されてなければ #endifまでで囲った処理を実行する、という意味なので、 これで、クラスの定義が1度しか呼ばれないことになる。

#ifndef  _PART_H_
#define  _PART_H_ //1回目のみ呼ばれる

namespace cocos2d {
    class Vec2; //cocos2d::Vec2というクラスがあるよ、という宣言。Vec2はポインタで持っているので定義はいらない
}

class Part : public cocos2d::Node
{
    
public:
    Part(cocos2d::Vec2* pos);
    ~Part();
    Part* getPos();
    void setPos(Part* pos);
private:
    cocos2d::Vec2* _pos;
};

#endif // _PART_H_

rand()

cppでランダムな数値を取得するにはrand()が使える

rand() % 3;

上記の例は、0〜2までの値を取得できる。ランダムな数値を3で割った割った余は、0〜2の数字だからだ。

Action

cocos2dxにもlibgdxと同じでActionの概念がある。

・NodeにrunActionメソッドを持っているのでそれに登録してあげればいい。

//3秒で180度回転。単位は「度」でOK
runAction(RotateBy::create(3, 180));

・上記のままだと等速で処理を行うのでだんだんはやくしたりとかしたい場合はイージングを設定する

runAction(EaseIn::create(RotateBy::create(3, 180), 2));

・単純にxxの処理をしたい、という場合はCallFuncNを使う

    auto print = CallFuncN::create([this](Node* node){
        printf("aaaaaa");
    });

・シーケンス 順次実行 Sequence

auto rotate = EaseIn::create(RotateBy::create(3, 180), 2);
auto scale = EaseIn::create(ScaleBy::create(1, 0.5f), 3);
auto action = Sequence::create(rotate, scale, NULL); //最後は必ずNULLを設定しないとダメ
    
runAction(action);

reference count should be greater than 0

リファレンスカウンタ周りのエラー。

基本的にcocos2d-xのクラスはRefクラスを継承していて、リファレンスカウンタの仕組みを使用している。

Node#createインスタンスを生成する((newはスコープで制限されてるので呼べない))とRef#autorelease状態のインスタンスを取得できるが

クラスのメンバ変数は、自分でreleaseさせたいので、自分でリファレンスカウンタを意識する必要がある。

これがちゃんとなっていない所為で上記のエラーメッセージが出てたのでメモ。

とはいえ、カウンタを操作するRef#retainRef#releaseは、自分で呼びたくないので、マクロを使います。

//Part.h

class Part : public cocos2d::Node
{
public:    
    CC_SYNTHESIZE_RETAIN(cocos2d::Sprite*, _sprite, Sprite);
};
}

CC_SYNTHESIZE_RETAIN で宣言すると、内部的にRef#retainを呼んでくれる。setter,getterも自動で作られる。

//Part.cpp

Part::Part(Vec2* pos) : _pos(pos), _sprite(NULL) //まずnullを初期設定
{
 //画像をセット。テクスチャアトラスからとってきてます。
    this->setSprite(Sprite::createWithSpriteFrameName("snake_down.png"));
}

Part::~Part()
{
    CC_SAFE_RELEASE_NULL(_sprite);
}

・デストラクタでCC_SAFE_RELEASE_NULL呼んで、Ref#releaseする。

2Dゲームの数学らへんのことまとめ

サイン、コサイン、タンジェント

#直角三角形の場合のはなし

直角以外の角度から長さの比率を求めることが出来る。

sinθ = 対辺 / 斜辺

→対辺が求まる。= Y座標にあたる。

cosθ = 隣辺 / 斜辺

→隣辺が求まる。= X座標にあたる。

tanθ = 対辺 / 隣辺

アークサイン、アークコサイン、アークタンジェント

サイン、コサイン、タンジェントの値(つまり長さの比率)から、角度を求めることが出来る。

libgdxではアークタンジェントを求めてくれる関数↓

比率ではなく、長さそのものがパラメータになっている。

float angle = MathUtils.atan2( screenY - 200, screenX -200 );

radian(ラジアン)とDegree(角度)

radian(ラジアン)はPIを使って表した角度。

90度 = PI / 2

180度 = PI

パラメータや戻り値になっている角度がradian(ラジアン)なのかDegree(角度)なのかは、わかっておかないといけない。

上に書いたMathUtils#atan2の戻りは、radian(ラジアン)。

それに対して、とく回転で使うActor#setRotationに渡すパラメータはDegree(角度)なので、ラジアンからDegree(角度)に変換する必要がある。

radian * MathUtils.radDeg;

上で変換出来る。逆にデジ→ラジの変換係数もMathUtilsにある。

ベクトル

数学的には向きと力のこと。ゲームプログラミングだと単純に座標(X, Y)とかで使われる。

libGDXではcom.badlogic.gdx.math.Vector2,com.badlogic.gdx.math.Vector3のクラスがこれにあたる。

ピタゴラスの定理

h2 = a2 + b2 h2 のルート が斜辺の長さ

ピタゴラスの定理を使って、斜辺の長さがわかる。libgdxだとVector2#lenがこれに相当する。

2点A,Bの距離を求めたい場合は、以下のようにすればよい。

AのVector.sub(BのVector) = AB差分Vector
2点の距離 = AB差分Vector#len()

上記ピタゴラスの定理では距離が求まるが、正確な距離が知りたいのではない場合(2つの距離を比べてどちらが長いか、だけを知るときなど)は ルートの演算をする前の2乗したそのままの値で比較すればいい。

ルートの処理は地味に思いので、それをしないでいいところではしないほうが処理が軽い。

libgdxではVector2#len2がこれにあたる。

単位ベクトル

上記ピタゴラスの定理では、2点の差の距離と向きがわかるが、

自分で速さを調整する時などのために、向きだけの情報がほしかったりする。

その為、単位ベクトルに変換する。この変換することを正規化する と言ったりもする。

libgdxではVector2#norがこの正規化する関数にあたる。

補完

点AからBへ移動するのに、少しずつ移動する、というようなのを補完という。

移動だけの話じゃなくて、赤から青にちょっとずつ変えたりとか。そういうのにも使える。

libgdxのActions#moveByとかは移動差分と時間を指定しているので、単純に距離を時間でわってちょっとずつ動かしたりしてる。そういうののことをいう。

libgdxのクラスでjsonを使う

今作ってるゲームで、データを外部ファイル化させたいなあ。となった。 xmlでもいいんだけど、今回はjsonで。

jsonといえば、jacksonが有名なのかな、と思いますがlibgdxにもjsonを扱うクラスがあります。

新たなjarを追加しなくていいですしあまり難しいことをしないならこちらでも十分だと思います。

前提

・公式wikiのここ↓の内容です。

https://github.com/libgdx/libgdx/wiki/Reading-%26-writing-JSON

・注意:今作っているゲームは、不思議なダンジョンっぽいゲームなこともあって、 ダンジョンの情報を外で持っておきたいという趣旨なので、コードもそれっぽいです。

javaクラス

public class DungeonDataList {
    public Array<Dungeon> dungeons;  //全ダンジョン情報
}

class Dungeon{
    char id;   //ダンジョンのID
    protected String name; //ダンジョンの名前
    private int floorNo = 0; //ダンジョンのフロア数

    @Override
    public String toString() {
        return "Dungeon [id=" + id + ", name=" + name + ", floorNo=" + floorNo + "]";
    }

    public void setFloorNo(int floorNo) {
        this.floorNo = floorNo;
    }
}

ダンジョンの情報(ダンジョンの名前と、ダンジョンのフロア数) = Dungeonクラス

全てのダンジョンの情報(Dungeonクラス)を配列でもったDungeonDataを用意しておきました。

Javaオブジェクト→JSON文字列

Json#toJson で変換するオブジェクトと、変換する型を指定。

public class ObjectToJsonSampleListener extends ApplicationAdapter {
    @Override
    public void create() {
        DungeonDataList obj = new DungeonDataList();
        obj.dungeons = new Array<>();
        Dungeon dungeon1 = new Dungeon();
        dungeon1.id = 'A';
        dungeon1.name = "Home";  //おうちダンジョン
        dungeon1.setFloorNo(5);
        obj.dungeons.add(dungeon1);

        Dungeon dungeon2 = new Dungeon();
        dungeon2.id = 'B';
        dungeon2.name = "Forest";    //森ダンジョン
        dungeon2.setFloorNo(3);
        obj.dungeons.add(dungeon2);

        Json json = new Json();
        String jsonText = json.toJson(obj, DungeonDataList.class );
        jsonText = json.prettyPrint( jsonText, setting);
        System.out.println( jsonText );
    }
}

・実行結果

{dungeons:[{id:A,name:Home,floorNo:5},{id:B,name:Forest,floorNo:3}]}

JSON文字列→Javaオブジェクト

Json#fromJson に変換する型とJSON文字列を指定。

この例では、jsonファイルを読んで*1、その文字列をオブジェクトに変換します。

public class JsonToObjectSampleListener extends ApplicationAdapter {

    @Override
    public void create() {
        String jsonText = Gdx.files.internal( "dungeon.json" ).readString();
        Json json = new Json();
        DungeonDataList obj = json.fromJson( DungeonDataList.class, jsonText );

        System.out.println("dungeon1: " + obj.dungeons.get(0));
        System.out.println("dungeon2: " + obj.dungeons.get(1));
    }
}

・出力結果

dungeon1: Dungeon [id=A, name=Home, floorNo=5]
dungeon2: Dungeon [id=B, name=Forest, floorNo=3]

型引数を持ったArray,String,int, char を柔軟に変換できています。

また、フィールドのスコープも関係なく(getter/setterを作成しなくても)変換できていることがわかります。

入出力の設定

public class ObjectToJsonSampleListener extends ApplicationAdapter {
    @Override
    public void create() {
        //〜データ詰める部分省略

        Json json = new Json();
        String jsonText = json.toJson(obj, DungeonData.class );
        
        JsonValue.PrettyPrintSettings setting = new JsonValue.PrettyPrintSettings();
        setting.outputType = JsonWriter.OutputType.json;
        jsonText = json.prettyPrint( jsonText, setting);
        System.out.println( jsonText );
    }
}

・実行結果

{
"dungeons": [
    {
        "id": "A",
        "name": "Home",
        "floorNo": 5
    },
    {
        "id": "B",
        "name": "Forest",
        "floorNo": 3
    }
]
}

Json#prettyPrint でjson文字列に変換する際に、インデントなど整形してくれます。

JsonValue.PrettyPrintSettings#outputType で任意の形式(上の例はjson)に指定することで(これをやらないと"がなかったりします)、 整形されたJSON文字列が出力されます。

型が不明確なとき

public Array<Dungeon> dungeons; //全ダンジョン情報

これまでの例では、リスト(Array)クラスのフィールドが、Dungeonクラスだと明確にジェネリクスで宣言していました。

ですが、これでは同じようなことをするクラスが出てきた場合に非常に不便です。idやnameはダンジョンの情報でなくても持っていることも多いと思います。

public Array<T extends BaseData> dungeons; 

なのでBaseDataというスーパークラスを用意しておいて、条件つきのジェネリクスを定義するとします。

この場合、型が明確ではない(BaseDataを継承するなんらかの子クラス)ため、JsonValueオブジェクトとして扱われます。

※型を指定しない場合も同じだった

json文字列をどの型にマッピングするかを指定したい場合は、Json#setElementTypeで指定することができます。

json.setElementType(BaseDataList.class, "dungeons", Dungeon2.class);

基本的なことはここまで。このあとは必要が出てきたら追記