iPhoneXが発売され、いわゆるアスペクト比2:1の端末が増えてきました
AndroidでもGaraxy S8、LG G8などが2:1に近いアスペクト比となっており、GoogleもAndroidアプリ開発者に対し、アスペクト比2:1以上の画面に対応することを促しています
例えば横スクロールのアクションゲームで、タブレット端末でよくある4:3の端末と
2:1の両対応を単純にしようとするとこのような画面になってしまいます
これだとステージの先、見えている敵キャラが違うためゲームの難易度に大きな違いが生じてしまいます
4:3のアスペクト比を基本にして、それよりも横長なら両端黒帯ってのは簡単な対応ですが、それは避けたいところですので、いくつか対応案を考えてみました
1つ目は2:1の横幅を基本として天井(空)の拡縮で対応する横幅固定の下寄せ
こんな感じ
見た目にかなり差が出ますが、見えているステージの範囲は同じになるので難易度に違いは出なくなるかと
2つ目は横スクロールでも天井がある場合や、シューティングの場合ステージの縦幅を変えるのは都合が悪い時に対応する横幅固定の真ん中寄せ
横幅は2:1を基本にして上下の地面と天井部分を拡縮します
ものによっては地面と天井の比率を変えてもよいかと思います
3つ目は2:1端末が増えてきたとはいえ、主流はまだまだ16:9のワイドなので、それを基本として調整するバランス型
横幅は16:9のワイドを基本にして、2:1端末では影響が出にくい自キャラの後ろ部分を拡縮してやります
4:3では上部分を拡縮して対応
以上3つの手法を紹介しましたが、やり方はまだまだあると思います
要はメインとなるステージの縦と横幅で変えられないところ、アスペクト比が変えられるのかというところを決めて、基本とする端末のアスペクト比からサイズ差を吸収する調整枠を決定するという流れになるかと思います
2017年11月21日火曜日
2017年11月10日金曜日
XMLで作るアニメーションするメニュー
新作アプリオタマーズ(仮)作ってます
タイトルメニューに階層があって、切り替えにアニメーションをつけてみたので紹介します
画面レイアウトのXMLがこちら
メニュー部分はFrameLayoutで直下のLinearLayoutに1階層目のボタン3つ
id="@+id/stageの部分が2階層目になっててvisible=goneで隠してます
次にAnimation用のXMLがこちら
動かすViewに上記のstartAnimationをセットしてやるだけです
アニメーションする前に消えてしまうので、AnimationListenerをimplementsして
onAnimationEndで消してます
res/layout/menu.xml
ほぼほぼXMLで済ませているので簡単です今回メニューに階層があるんでアニメーションするようにしてみた— Anadreline@新作アプリ開発中 (@Anadreline) 2017年11月9日
わりといい感じ、あとでブログにまとめよう pic.twitter.com/Iuqc59yzCU
画面レイアウトのXMLがこちら
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <ImageView <FrameLayout <LinearLayout <ImageButton android:id="@+id/setting" <ImageButton android:id="@+id/mission" ・・・ </LinearLayout> </FrameLayout> </LinearLayout>くっそ長いので全文は下のほうに追いやりました
メニュー部分はFrameLayoutで直下のLinearLayoutに1階層目のボタン3つ
id="@+id/stageの部分が2階層目になっててvisible=goneで隠してます
次にAnimation用のXMLがこちら
// 1階層目メニュースライドイン // res/anim/menu_in.xml <?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/decelerate_interpolator" android:fromXDelta="-100%p" android:toXDelta="0%p" android:fillAfter="true" android:duration="800" /> // 1階層目メニュースライドアウト // res/anim/menu_out.xml <?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/decelerate_interpolator" android:fromXDelta="0%p" android:toXDelta="120%p" android:fillAfter="true" android:duration="800" /> // 2階層目メニューズームイン // res/anim/zoom_in.xml <?xml version="1.0" encoding="utf-8"?> <scale xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/decelerate_interpolator" android:fromXScale="0" android:toXScale="1.0" android:fromYScale="0.0" android:toYScale="1.0" android:pivotX="50%" android:pivotY="50%" android:fillAfter="true" android:duration="600" />で、ボタンを押したときの処理がこちら
動かすViewに上記のstartAnimationをセットしてやるだけです
@Override public void onClick(View v) {
switch (v.getId()) {
case R.id.mission : //2階層目表示
menu_off = true;
findViewById(R.id.stage).startAnimation(AnimationUtils.loadAnimation(this, R.anim.zoom_in));
findViewById(R.id.setting).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_out));
findViewById(R.id.mission).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_out));
findViewById(R.id.challenge).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_out));
break;
case R.id.title1 : //1階層目に戻る
menu_off = false;
findViewById(R.id.stage).clearAnimation(); //これやらんとなぜか変に表示が残る
findViewById(R.id.stage).setVisibility(View.GONE);
findViewById(R.id.setting).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_in));
findViewById(R.id.setting).setVisibility(View.VISIBLE);
findViewById(R.id.mission).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_in));
findViewById(R.id.mission).setVisibility(View.VISIBLE);
findViewById(R.id.challenge).startAnimation(AnimationUtils.loadAnimation(this, R.anim.menu_in));
findViewById(R.id.challenge).setVisibility(View.VISIBLE);
break;
}
}
メニューを消すときにstartAnimation直後に.setVisibility(View.GONE)をやってしまうとアニメーションする前に消えてしまうので、AnimationListenerをimplementsして
onAnimationEndで消してます
public class Menu extends Activity implements View.OnClickListener, Animation.AnimationListener {
@Override public void onAnimationStart(Animation animation) { }
@Override public void onAnimationRepeat(Animation animation) { }
@Override public void onAnimationEnd(Animation animation) {
if (menu_off) {
findViewById(R.id.setting).setVisibility(View.GONE);
findViewById(R.id.mission).setVisibility(View.GONE);
findViewById(R.id.challenge).setVisibility(View.GONE);
}
}
以上res/layout/menu.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/title_back"
android:gravity="center_horizontal"
android:orientation="vertical" >
<ImageView
android:scaleType="fitStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="32dp"
android:src="@drawable/title" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center">
<ImageButton
android:id="@+id/setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="36dp"
android:background="@null"
android:onClick="onClick"
android:src= "@drawable/menu_sg"/>
<ImageButton
android:id="@+id/mission"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="36dp"
android:background="@null"
android:onClick="onClick"
android:src= "@drawable/menu_mi"/>
<ImageButton
android:id="@+id/challenge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="36dp"
android:background="@null"
android:onClick="onClick"
android:src= "@drawable/menu_ch"/>
</LinearLayout>
<LinearLayout
android:id="@+id/stage"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:visibility="gone">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal">
<LinearLayout
android:id="@+id/stage1"
android:tag="1"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:onClick="onClick"
android:gravity="center_horizontal">
<ImageView
android:scaleType="matrix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/mission_u"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Stage1"
android:gravity="center"
android:textColor="#000"
android:textSize="12sp"
android:ellipsize="end"/>
</LinearLayout>
<LinearLayout
android:id="@+id/stage2"
android:tag="2"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:onClick="onClick"
android:gravity="center_horizontal">
<ImageView
android:scaleType="matrix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/mission_l"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Stage2"
android:gravity="center"
android:textColor="#000"
android:textSize="12sp"
android:ellipsize="end"/>
</LinearLayout>
<LinearLayout
android:id="@+id/stage3"
android:tag="3"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:onClick="onClick"
android:gravity="center_horizontal">
<ImageView
android:scaleType="matrix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/mission_u"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Stage3"
android:gravity="center"
android:textColor="#000"
android:textSize="12sp"
android:ellipsize="end"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal">
<LinearLayout
android:id="@+id/stage4"
android:tag="4"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:onClick="onClick"
android:gravity="center_horizontal">
<ImageView
android:scaleType="matrix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/mission_u"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Stage4"
android:gravity="center"
android:textColor="#000"
android:textSize="12sp"
android:ellipsize="end"/>
</LinearLayout>
<LinearLayout
android:id="@+id/stage5"
android:tag="5"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:onClick="onClick"
android:gravity="center_horizontal">
<ImageView
android:scaleType="matrix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/mission_u"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Stage5"
android:gravity="center"
android:textColor="#000"
android:textSize="12sp"
android:ellipsize="end"/>
</LinearLayout>
<LinearLayout
android:id="@+id/stage6"
android:tag="6"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:onClick="onClick"
android:gravity="center_horizontal">
<ImageView
android:scaleType="matrix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/mission_u"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Stage6"
android:gravity="center"
android:textColor="#000"
android:textSize="12sp"
android:ellipsize="end"/>
</LinearLayout>
</LinearLayout>
<ImageButton
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="36dp"
android:background="@null"
android:onClick="onClick"
android:src= "@drawable/menu_tt"/>
</LinearLayout>
</FrameLayout>
</LinearLayout>
2015年7月1日水曜日
パッティングゲームの製作(1万件超データの格納方法)
さて、前回は傾斜面を転がるボールが曲がるアルゴリズムを作成しましたが、その中でボール位置の傾斜角度を利用して計算してます
そのためグリーンの傾斜角データを用意してやらねばなりません
とりあえずグリーンの大きさは横10m×縦6mとして、まずは高度を濃度で表現した画像を作成しました
これを縦横共に100分割した各点の高度を求めて、隣接点の高度からXY方向の傾斜角を取得します
こうして取得したXY方向の各点の傾斜角が100×60×2で12000個になりました
この12000個のデータをリアルタイムで利用しなければならんのですが、とりあえずCSVファイルにしてアプリ起動時に読込んでみましたが、読込みが遅い・・・4~5秒ほどかかってしまいます
データベースに入れるのもなんだし、どうしたもんかなぁ悩んでみて、もういっそのこともうベタで書いてみるかとスタティック変数にぶち込んでみました
ひどいですね、良い子はマネしてはいけません
しかしこれは劇的に早くなり、かなりよさそうなんで、もうこれでいっちゃいます
そのためグリーンの傾斜角データを用意してやらねばなりません
とりあえずグリーンの大きさは横10m×縦6mとして、まずは高度を濃度で表現した画像を作成しました
これを縦横共に100分割した各点の高度を求めて、隣接点の高度からXY方向の傾斜角を取得します
こうして取得したXY方向の各点の傾斜角が100×60×2で12000個になりました
この12000個のデータをリアルタイムで利用しなければならんのですが、とりあえずCSVファイルにしてアプリ起動時に読込んでみましたが、読込みが遅い・・・4~5秒ほどかかってしまいます
データベースに入れるのもなんだし、どうしたもんかなぁ悩んでみて、もういっそのこともうベタで書いてみるかとスタティック変数にぶち込んでみました
ひどいですね、良い子はマネしてはいけません
しかしこれは劇的に早くなり、かなりよさそうなんで、もうこれでいっちゃいます
2015年6月16日火曜日
パッティングゲームの製作(斜面でボールが曲がるアルゴリズム)
前回グリーンを転がるボールが止まるアルゴリズムのやったんで、今度は地面の傾斜でボールが曲がるアルゴリズムを作ってみたいと思います
なぜボールが曲がるかっていうと、重力が働いているからですね
図にするとこんな感じ
傾斜角θの時、x方向にg・sinθの力が加わり、ボールが曲がります
これをソースに起こしてみましょう
なぜボールが曲がるかっていうと、重力が働いているからですね
図にするとこんな感じ
傾斜角θの時、x方向にg・sinθの力が加わり、ボールが曲がります
これをソースに起こしてみましょう
public class Ball {
final public static int G = 9800; //重力加速度(mm/ss)
final public static float u = 0.075f; //摩擦係数
public Plot position = new Plot(); //位置
public int speed; //並進速度(mm/s)
public double angle; //進行方向(Radian)
//移動処理(interval:経過時間(ms))
public void move(long interval) {
//摩擦による減速処理
speed -= (u * G * interval) / 1000;
//速度をXY成分に分割
int xSpeed = (int)(speed * Math.cos(angle));
int ySpeed = (int)(speed * Math.sin(angle));
double xa = getXTilt(prev.x, prev.y); //X方向の傾斜角取得
double ya = getYTilt(prev.x, prev.y); //Y方向の傾斜角取得
xSpeed -= (G * Math.sin(xa) * interval) / 1000; //X方向に働く重力による加速
ySpeed -= (G * Math.sin(ya) * interval) / 1000; //Y方向に働く重力による加速
angle = getAngle(0f, 0f, xSpeed, ySpeed); //重力により曲がった進行方向を取得
//XYの速度から位置を更新
position.x += (xSpeed * interval) / 1000;
position.y += (ySpeed * interval) / 1000;
}
}
こんな感じでボールが転がる処理は完成
2015年6月10日水曜日
新作アプリ、パッティングゲームの製作(転がるボールのアルゴリズム)
以前の新作アプリのことを記事で書きましたが、もうだいぶ出来てきました
ベース部分はほぼEasy Basketballなのでかなり楽が出来ましたが、グリーン上を転がるボールのアルゴリズムを一から作る必要がありました
せっかくなので製作過程を書いていきます
まずは任意の初速で放たれたボールがグリーン上を転がり止まるという基本的な動きを考えます
なぜボールが止まるかというと当然地面から受ける摩擦で止まるので、摩擦係数がわかれば転がるボールのアルゴリズムが組み立てられると考えられます
調べてみるとグリーンの速さを測る道具にスティンプメータという滑り台のような道具があるようで、これでボールを射出すると初速1.83m/sで放たれどのくらい転がるかでグリーンの早さを測ることが出来ます
これで測った時の典型的な速さのグリーンの転がる距離が2.286mということなので、これを水平面上を摩擦を受けて並進する物体ということで、運動量保存の法則に入れてみます

m:ボールの重さ
v:初速(m/s)
μ:摩擦係数
g:重力加速度(m/s2)
d:移動距離(m)
ここから摩擦係数μ= 0.075が求められますね
ところが、よく調べてみるとこの計算は大間違いのようで
参考:蜂の子:グリーンの転がり摩擦係数μ
正しくは回転エネルギーを用いて計算せねばならず正しい摩擦係数(転がり摩擦係数)はμ= 0.02989715571ということです
しかしこれを使うとなると移動の計算に角速度を用いねばならず、ちょっとめんどくさいゲーム上では並進速度と摩擦係数μ= 0.075を用いた計算で特に支障はないのでこちらを使うことにします
細かいことを言うとパットにはロフトがついていて打出し時にはボールは少し浮いているので、摩擦を受けない時間があったりするのですが、まあ細かいことはいいそれほどゲーム性に影響はないのでこれでよしとします
ベース部分はほぼEasy Basketballなのでかなり楽が出来ましたが、グリーン上を転がるボールのアルゴリズムを一から作る必要がありました
せっかくなので製作過程を書いていきます
まずは任意の初速で放たれたボールがグリーン上を転がり止まるという基本的な動きを考えます
なぜボールが止まるかというと当然地面から受ける摩擦で止まるので、摩擦係数がわかれば転がるボールのアルゴリズムが組み立てられると考えられます
調べてみるとグリーンの速さを測る道具にスティンプメータという滑り台のような道具があるようで、これでボールを射出すると初速1.83m/sで放たれどのくらい転がるかでグリーンの早さを測ることが出来ます
これで測った時の典型的な速さのグリーンの転がる距離が2.286mということなので、これを水平面上を摩擦を受けて並進する物体ということで、運動量保存の法則に入れてみます
m:ボールの重さ
v:初速(m/s)
μ:摩擦係数
g:重力加速度(m/s2)
d:移動距離(m)
ここから摩擦係数μ= 0.075が求められますね
ところが、よく調べてみるとこの計算は大間違いのようで
参考:蜂の子:グリーンの転がり摩擦係数μ
正しくは回転エネルギーを用いて計算せねばならず正しい摩擦係数(転がり摩擦係数)はμ= 0.02989715571ということです
しかしこれを使うとなると移動の計算に角速度を用いねばならず、
public class Ball {
final public static int G = 9800; //重力加速度(mm/ss)
final public static float u = 0.075f; //摩擦係数
public Plot position = new Plot(); //位置
public int speed; //並進速度(mm/s)
public double angle; //進行方向(Radian)
//移動処理(interval:経過時間(ms)
public void move(long interval) {
//摩擦による減速処理
speed -= (u * G * interval) / 1000;
//速度をXY成分に分割
int xSpeed = (int)(speed * Math.cos(angle));
int ySpeed = (int)(speed * Math.sin(angle));
//XYの速度から位置を更新
position.x += (xSpeed * interval) / 1000;
position.y += (ySpeed * interval) / 1000;
}
}
細かいことを言うとパットにはロフトがついていて打出し時にはボールは少し浮いているので、摩擦を受けない時間があったりするのですが、
2015年6月4日木曜日
PCの突然の死!!!からの~NewPCとともに開発再開
昨年末長年使っていた開発用PCが突然電源がつかなくなってしまいました
まあ相当古いPCだったのでこれを機に新しいPCを組み直すことに
いろいろ考えてるうちに結局3月までかかってしまいました…
ケース以外全とっかえのNewPCの仕様はこちら
そこそこがんばりました
前が前(Core2Duo)だっただけにめっちゃ早く感じます
これで前はきつかったAndroidStudioや重いソフトもキビキビ動くのでいろいろとやれる幅が広がりそうです
休業中に色々とアイデアもたまったので、いろいろと作っていこうかと思います
まあ相当古いPCだったのでこれを機に新しいPCを組み直すことに
いろいろ考えてるうちに結局3月までかかってしまいました…
ケース以外全とっかえのNewPCの仕様はこちら
| 種別 | メーカー | パーツ |
|---|---|---|
| CPU | Intel | Core i5 4690 |
| メモリ | ADATA | DDR3 4G*2 |
| MB | ASUS | H97-PRO |
| SSD | Crucial | BX100 256GB |
| HDD | WD | WD20EZRX 2TB |
| 電源 | 玄人志向 | SS600W/85 |
| OS | OS | Window7 64bit |
そこそこがんばりました
前が前(Core2Duo)だっただけにめっちゃ早く感じます
これで前はきつかったAndroidStudioや重いソフトもキビキビ動くのでいろいろとやれる幅が広がりそうです
休業中に色々とアイデアもたまったので、いろいろと作っていこうかと思います
2014年10月21日火曜日
Android ADT 22.6.0以降はEclipse Heliosでは動かない
いやいやいや、いまだにHeliosて!Heliosて!
と思われるでしょうが
6年物のCore2DuoのPCでがんばってる身としては、なるべく軽くて、枯れた環境でやっていたかったのです・・・
ADT 22.6.0(March 2014) Dependencies:
今日からEclipse Keplerでがんばりたいと思います
と思われるでしょうが
6年物のCore2DuoのPCでがんばってる身としては、なるべく軽くて、枯れた環境でやっていたかったのです・・・
ADT 22.6.0(March 2014) Dependencies:
- Java 1.6 or higher is required.
- Eclipse Indigo (Version 3.7.2) or higher is required.
今日からEclipse Keplerでがんばりたいと思います
2014年6月4日水曜日
AndroidでTwitter公式の投稿Activityを取得(新旧バージョン対応)
前回Androidアプリからツイートする4つの方法を紹介しましたが、その中でPackageManagerを利用してピンポイントで公式クライアントアプリを呼び出す方法を紹介しました
しかし、この方法は呼出し時に投稿に使われるActivityまで指定しなければならないため、公式クライアントの更新により変更されてしまった場合使えなくなります
実際過去2回ほどありました
ここではその2回の変更に対応してユーザーの公式クライアントが古いバージョンの場合にも対応できる方法を紹介します
内容はいたって簡単で、パッケージ名は不変なので"com.twitter.android"でGET_ACTIVITIESによりActivityの一覧を取得して、既知の3つのActivity名と照合します
あとはこれで取得したActivity名を
intent.setClassName("com.twitter.android", 取得Activity名);
とセットして明示的インテントで呼べばオッケーです
しかし、この方法は呼出し時に投稿に使われるActivityまで指定しなければならないため、公式クライアントの更新により変更されてしまった場合使えなくなります
実際過去2回ほどありました
ここではその2回の変更に対応してユーザーの公式クライアントが古いバージョンの場合にも対応できる方法を紹介します
public static String getTwitterClass(Context context) {
String twitterClass = null;
PackageManager pm = context.getPackageManager();
String packagename = "com.twitter.android";
String class1 = "com.twitter.android.PostActivity";
String class2 = "com.twitter.applib.composer.TextFirstComposerActivity";
String class3 = "com.twitter.android.composer.TextFirstComposerActivity";
try {
ActivityInfo[] activities = pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES).activities;
for(int i = 0; i < activities.length; i++) {
if (activities[i].name.equals(class1)) {
twitterClass = class1;
break;
} else if (activities[i].name.equals(class2)) {
twitterClass = class2;
break;
} else if (activities[i].name.equals(class3)) {
twitterClass = class3;
break;
}
}
} catch (PackageManager.NameNotFoundException e) {
//Twitter is not install
}
return twitterClass ;
}
※Twitter バージョン5.8.0対応内容はいたって簡単で、パッケージ名は不変なので"com.twitter.android"でGET_ACTIVITIESによりActivityの一覧を取得して、既知の3つのActivity名と照合します
あとはこれで取得したActivity名を
intent.setClassName("com.twitter.android", 取得Activity名);
とセットして明示的インテントで呼べばオッケーです
2014年6月2日月曜日
AndroidアプリからTwitter公式クライアントでツイートする4つの方法
今回EasyBowlingとBowlingScoreBookにSNS投稿機能をつけましたが、BowlingScoreBookでは特に「Twitterで投稿」ボタンを付けました
その時に調べたTwitterの公式クライアントを呼びだしてツイートする4つの方法です
以下に各手法の特徴をまとめてみました
その時に調べたTwitterの公式クライアントを呼びだしてツイートする4つの方法です
- 暗黙的Intentで投稿 Intent.ACTION_SENDで暗黙的Intent呼び出しによる投稿です
- 公式アプリのPackage、Classを指定して投稿 PackageManagerで公式クライアントをピンポイントで呼び出します
- URIスキームを指定して投稿 WEBページからアプリを呼び出すのに使用するURIスキームを利用して呼び出します
- URL(http://twitter.com/)を利用して投稿 公式クライアントかWEBブラウザが呼び出されます ブラウザの場合、"http://twitter.com/"の投稿画面が呼び出されます
ACTION_SENDと"text/plain"に反応するアプリがすべて出るので公式以外のクライアントアプリ、Twitterに関係のないアプリも出てきます
public static void tweetFile1(Context context, String message) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, message);
((Activity)context).startActivity(intent);
}
公式以外のクライアントアプリ以外は反応しません
公式の投稿用Activityが変わった場合には修正が必要となります(過去2回程変更されている)
public static void tweetFile2(Context context, String message) {
String twitterClass = "com.twitter.android.composer.TextFirstComposerActivity";
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, message);
intent.setClassName("com.twitter.android", twitterClass);
((Activity)context).startActivity(intent);
} catch (PackageManager.NameNotFoundException e) {
//Toast("Not found com.twitter.android")
}
}
公式以外でも"twitter://"のスキームに対応しているアプリが呼び出されます
public static void tweetFile3(Context context, String message) {
String url = "twitter://post?message=" + message;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setType("text/plain");
intent.setData(Uri.parse(url));
((Activity)context).startActivity(intent);
}
public static void tweetFile4(Context context, String message) {
String url = "http://twitter.com/share?text=" + message;
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setType("text/plain");
intent.setData(Uri.parse(url));
((Activity)context).startActivity(intent);
}
以下に各手法の特徴をまとめてみました
| 投稿方法 | 互換 クライ アント | 画像 投稿 | 備 考 |
|---|---|---|---|
| 1.暗黙的Intent | ○ | ○ | Twitter関連アプリ以外もでる |
| 2.Package指定 | × | ○ | 公式Twitterクライアントのみでる |
| 3.URIスキーム指定 | △ | × | 互換クライアントはスキーム対応アプリのみ |
| 4.URL(HTTP)指定 | × | × | 公式クライアントがなくてもブラウザで投稿できる |
※2014/06/02現在
2014年4月28日月曜日
SurfaceViewで点線(破線)を描画する
新作アプリも最終調整段階に来ました
前にテコ入れ編 操作感をなんとかするという記事で操作領域を画面全体にしたことで、シュート方向がわかりにくくなってしまいました
その対策としてボールを引っ張った時のガイド線を入れてみようと思います
今回は簡単にPaint.setPathEffectの機能を使った点線を描画します
ソースはこんな感じ
描画した感じはこれ
これの注意点としてはsetStrokeWidthもそうですが指定がピクセル値ですので、指定値が同じだと端末のdpiによって見え方が変わってしまいますので注意しましょう
と言いつつ今回は方向がわかればいいので気にしない・・・
前にテコ入れ編 操作感をなんとかするという記事で操作領域を画面全体にしたことで、シュート方向がわかりにくくなってしまいました
その対策としてボールを引っ張った時のガイド線を入れてみようと思います
今回は簡単にPaint.setPathEffectの機能を使った点線を描画します
ソースはこんな感じ
Paint paint = new Paint();
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(2f);
paint.setColor(Color.BLACK);
paint.setAntiAlias(true);
paint.setPathEffect(new DashPathEffect(new float[]{ 2.0f, 2.0f }, 0));
canvas.drawLine(100, 100, 100, 200, paint);
描画した感じはこれ
これの注意点としてはsetStrokeWidthもそうですが指定がピクセル値ですので、指定値が同じだと端末のdpiによって見え方が変わってしまいますので注意しましょう
と言いつつ今回は方向がわかればいいので気にしない・・・
2014年4月22日火曜日
iOS/Android両対応の開発環境のまとめ
4/22日エンバカデロ・テクノロジーズは「Delphi」「C++Builder」の流れを汲むマルチデバイス対応アプリ開発プラットフォーム「Appmethod」を提供開始したようです
http://codezine.jp/article/detail/7745
いい機会なので iOS/Android両対応の主な開発環境をまとめてみました
色々と選択肢が増えるのは開発者にとってはありがたいことですね
Appmethod
概要:「Delphi」「C++Builder」の流れを汲むマルチデバイス対応アプリ開発プラットフォーム
開発言語:C++, Object Pascal(Delphi言語)
有料/無料
http://www.appmethod.com/jp/
Xamarin
概要:C#言語によるクロスプラットフォーム(iOS/Android)なモバイルアプリケーションの開発環境
Microsoft Visual Studioを利用して開発することが出来る
開発言語:C#
無料
http://xamarin.com/
Adobe AIR
概要:アドビシステムズが開発する複数のオペレーティングシステムに対応したFlashベースのアプリケーション実行環境
開発言語:ActionScript, HTML, JavaScript
有料/無料
http://get.adobe.com/jp/air/
Unity
概要:様々なプラットフォームで動作する高度な 3D アプリケーションを制作することが出来る、統合型のゲーム開発環境
開発言語:C#, JavaScript, Boo
有料/無料
http://japan.unity3d.com/
Cocos2d-X
概要:Zhe Wang氏によって開発され、オープンソースかつ無料の2Dゲームエンジン
開発言語:C++
無料
http://www.cocos2d-x.org/
PhoneGap
概要:Adobe社により公開されているHTML5・CSS・JavaScriptで様々なプラットフォームに対応するアプリケーションを開発するフレームワーク、各デバイスのカメラやGPS,電話帳も利用できる
開発言語:HTML5, CSS, JavaScript
無料
http://phonegap.com/
Titanium Mobile
概要:HTML・JavaScriptを利用してネイティブアプリを作成するオープンソースの開発環境、ネイティブUI,デバイスのカメラやGPS,電話帳も利用できる
開発言語:HTML, CSS, JavaScript
無料
http://www.appcelerator.com/titanium/
monaca
概要:クラウドサービスを利用して、アプリ開発の一連の作業をすべてWebブラウザを通じて行うことができる開発環境
開発言語:HTML5, CSS, JavaScript
有料/無料
http://monaca.mobi/ja/
http://codezine.jp/article/detail/7745
いい機会なので iOS/Android両対応の主な開発環境をまとめてみました
色々と選択肢が増えるのは開発者にとってはありがたいことですね
Appmethod
概要:「Delphi」「C++Builder」の流れを汲むマルチデバイス対応アプリ開発プラットフォーム
開発言語:C++, Object Pascal(Delphi言語)
有料/無料
http://www.appmethod.com/jp/
Xamarin
概要:C#言語によるクロスプラットフォーム(iOS/Android)なモバイルアプリケーションの開発環境
Microsoft Visual Studioを利用して開発することが出来る
開発言語:C#
無料
http://xamarin.com/
Adobe AIR
概要:アドビシステムズが開発する複数のオペレーティングシステムに対応したFlashベースのアプリケーション実行環境
開発言語:ActionScript, HTML, JavaScript
有料/無料
http://get.adobe.com/jp/air/
Unity
概要:様々なプラットフォームで動作する高度な 3D アプリケーションを制作することが出来る、統合型のゲーム開発環境
開発言語:C#, JavaScript, Boo
有料/無料
http://japan.unity3d.com/
Cocos2d-X
概要:Zhe Wang氏によって開発され、オープンソースかつ無料の2Dゲームエンジン
開発言語:C++
無料
http://www.cocos2d-x.org/
PhoneGap
概要:Adobe社により公開されているHTML5・CSS・JavaScriptで様々なプラットフォームに対応するアプリケーションを開発するフレームワーク、各デバイスのカメラやGPS,電話帳も利用できる
開発言語:HTML5, CSS, JavaScript
無料
http://phonegap.com/
Titanium Mobile
概要:HTML・JavaScriptを利用してネイティブアプリを作成するオープンソースの開発環境、ネイティブUI,デバイスのカメラやGPS,電話帳も利用できる
開発言語:HTML, CSS, JavaScript
無料
http://www.appcelerator.com/titanium/
monaca
概要:クラウドサービスを利用して、アプリ開発の一連の作業をすべてWebブラウザを通じて行うことができる開発環境
開発言語:HTML5, CSS, JavaScript
有料/無料
http://monaca.mobi/ja/
2014年4月14日月曜日
新作Androidアプリ開発 インスト・即消し対策 トレーニングモードの追加
本業が一息ついて気が抜けてしまいました
いい加減早く新作アプリをリリースしたい
さて、アプリの有効インストール率を上げるためにアプリを消されるのはいつか?ということを考えると一番多いのがインストール直後です
その削除に至る理由としては
1.に対してはHow toやプレイ動画の提示、2.に対してはチュートリアルやTips、トレーニングモード等を用意するといいでしょう
3.についてはとにかく企画からデザインまでがんばるしかないと・・・
というわけで2.対策として、トレーニングモードを追加しました
実装はこんな感じ
いい加減早く新作アプリをリリースしたい
さて、アプリの有効インストール率を上げるためにアプリを消されるのはいつか?ということを考えると一番多いのがインストール直後です
その削除に至る理由としては
- やり方がよくわからない
- どうやればうまくいくのかよくわからない
- しょぼい、おもしろくない
1.に対してはHow toやプレイ動画の提示、2.に対してはチュートリアルやTips、トレーニングモード等を用意するといいでしょう
3.についてはとにかく企画からデザインまでがんばるしかないと・・・
というわけで2.対策として、トレーニングモードを追加しました
実装はこんな感じ
2014年3月21日金曜日
新作Androidアプリ開発 GUIを盛る編 ゲージのような表示
さて新作アプリの開発ですが、 4回にわたるテコ入れ編でクリティカルな問題に対しての対策が出来ました
あとは完成に向けてひた走るだけ!?
というわけで今回から見た目部分の改良でGUIを盛る編といきたいと思います
まずは右上に表示していた残り時間の表示がスコアの表示と同じじゃわかりにくいし、なんかしょぼいのでもう少し視覚的に残り時間がわかりやすいようにしたいと思います
完成イメージはこう
これをどうやって描画したらいいかなぁと考えました
やり方はいろいろでしょうが今回は丸いゲージの部分の画像をフルで用意して、それを扇形のclipPathでマスクして必要部分だけを描く方法を取りました
パーツを一個用意して回転させながら60個描く方法もありますが、clipPathの方法なら切れ目がないようなアナログ的なゲージにも対応できる思います
コード的には以下のような感じ
clipPathは描画したい部分の指定になるので、addArc()の開始角と終了角をずらしながらセットしてやります
わりといい感じにできました
あとは完成に向けてひた走るだけ!?
というわけで今回から見た目部分の改良でGUIを盛る編といきたいと思います
まずは右上に表示していた残り時間の表示がスコアの表示と同じじゃわかりにくいし、
完成イメージはこう
これをどうやって描画したらいいかなぁと考えました
やり方はいろいろでしょうが今回は丸いゲージの部分の画像をフルで用意して、それを扇形のclipPathでマスクして必要部分だけを描く方法を取りました
パーツを一個用意して回転させながら60個描く方法もありますが、clipPathの方法なら切れ目がないようなアナログ的なゲージにも対応できる思います
コード的には以下のような感じ
int time; //残り時間 int rect; //ゲージの大きさのRect Bitmap gauge; //フルゲージ画像 Path clip = new Path(); clip.moveTo(rect.centerX(), rect.centerY()); clip.addArc(rect, 267 + ((60 - time) * 6), 360 - ((60 - time) * 6)); clip.lineTo(rect.centerX(), rect.centerY()); canvas.save(); canvas.clipPath(clip); gauge.draw(canvas); canvas.restore();このブログで以前、SurfaceViewで描画範囲を限定するでやった方法と同じですね
clipPathは描画したい部分の指定になるので、addArc()の開始角と終了角をずらしながらセットしてやります
わりといい感じにできました
2014年3月6日木曜日
新作Androidアプリ開発 テコ入れ編 操作感をなんとかする
さてテストプレイ編で課題がいくつかあがったので、それらの対策をしていきたいと思います
まずはじめにあがった
1.一番大事な操作感、すべてが小さくて、ちまちましてイライラする
というところ、 ゲームにとって操作は生命線です
操作するだけで楽しい、うまくいくと気持ちがいいという感覚
そういった操作感がゲームの面白さに直結するため非常に重要なところ
今回原因はすべてが小さいところに起因するわけなんで、単純に大きくしてやろうと考えました
グリップを表示して引っ張って放してシュートという方式は直感的でいいのですが、グリップの分どうしても引きしろ範囲を大きくとらねばなりません
なので思い切ってグリップを廃止、画面全体を操作対象にしました
全体的にかなり大きくなったので、操作感は大分良くなりました
ただグリップ式よりも方向が決めにくくなった感があるのでガイドなど表示する必要があるかも
まずはじめにあがった
1.一番大事な操作感、すべてが小さくて、ちまちましてイライラする
というところ、 ゲームにとって操作は生命線です
操作するだけで楽しい、うまくいくと気持ちがいいという感覚
そういった操作感がゲームの面白さに直結するため非常に重要なところ
今回原因はすべてが小さいところに起因するわけなんで、単純に大きくしてやろうと考えました
グリップを表示して引っ張って放してシュートという方式は直感的でいいのですが、グリップの分どうしても引きしろ範囲を大きくとらねばなりません
なので思い切ってグリップを廃止、画面全体を操作対象にしました
全体的にかなり大きくなったので、操作感は大分良くなりました
ただグリップ式よりも方向が決めにくくなった感があるのでガイドなど表示する必要があるかも
2014年3月3日月曜日
新作Androidアプリ開発 テストプレイ編 作りたいから作った結果がこれ・・・
さて一通りの動きは実装したのでここらでざっくりとテストプレイをしてみたいと思います
本当は誰かほかの人に頼んで意見や感想をもらったりしたほうがいいのでしょうが、なかなか難しいので自分で心を鬼にして厳しいユーザー視点に立ってプレイします
ここでアプリのコンセプト、方向性、課題を厳しくチェックします
んークソゲーか?
見た感じのしょぼさはまだ仮画像なんでおいといて・・・
ゲームとしてどうなのこれ、成り立ってるのか!?
誰だよこの企画を作ったやつは?オレカ・・・しかもこんなことまで言っている
http://anadreline.blogspot.com/2014/02/blog-post.html
>もしこれが企業での企画なら絶対通らないでしょう
>今更バスケットのシュートゲームかよと一蹴されて終わる
>しかしここが個人開発者の最高なところ
>作りたいから作るのです
作りたいから作った結果がこれか・・・
いやしかしそうだ、作りたいんだった、脳内企画ではもっと面白かったはず
まだどうにかする余地はきっとあると思う
とりあえず現状の問題点を挙げよう
残念ながらこのままリリースへ向かえるような状態ではない
とりあえずこれらに対して何かしらの解決策を見出して、根本的にてこ入れを行う必要がある
はあ・・・ゲーム作りって難しいですね
本当は誰かほかの人に頼んで意見や感想をもらったりしたほうがいいのでしょうが、なかなか難しいので自分で心を鬼にして厳しいユーザー視点に立ってプレイします
ここでアプリのコンセプト、方向性、課題を厳しくチェックします
んー
見た感じのしょぼさはまだ仮画像なんでおいといて・・・
ゲームとしてどうなのこれ、成り立ってるのか!?
誰だよこの企画を作ったやつは?オレカ・・・しかもこんなことまで言っている
http://anadreline.blogspot.com/2014/02/blog-post.html
>もしこれが企業での企画なら絶対通らないでしょう
>今更バスケットのシュートゲームかよと一蹴されて終わる
>しかしここが個人開発者の最高なところ
>作りたいから作るのです
作りたいから作った結果がこれか・・・
いやしかしそうだ、作りたいんだった、脳内企画ではもっと面白かったはず
まだどうにかする余地はきっとあると思う
とりあえず現状の問題点を挙げよう
- 一番大事な操作感、すべてが小さくて、ちまちましてイライラする
- ボールの動きがわかりにくい、特に高低差、ゴールしたのかどうか、たまに変な挙動
- 60秒で何ゴールできるかというルールではスコアの頭打ちが早いのではないか
- 何度もやりたくなる気がおきない
残念ながらこのままリリースへ向かえるような状態ではない
とりあえずこれらに対して何かしらの解決策を見出して、根本的にてこ入れを行う必要がある
はあ・・・ゲーム作りって難しいですね
2014年2月26日水曜日
新作Androidアプリ開発 ボールをランダムに配置しよう編
ボールを操作で投げれるようになったので、今回はボールを任意のエリアにランダムに配置してみましょう
配置したいエリアはというと、距離的にフリースローからスリーポイントまでの間にあたる扇状の青の部分です
操作の都合上、リングの左右側の鋭角を10°程削っています
これをどう計算してランダムな座標を生成するか
もし対象とするエリアが長方形だったりすると話は簡単です
XとY軸の対象範囲をRandom.nextInt(value)するだけ
気持ち悪いですね
今回は変形扇形のエリアではいろいろと方法はあると思いますが、このような考え方で生成しました
赤枠部分フリースローラインからスリーポイントラインまでの範囲でランダムにセットした座標を、対象範囲角をランダムにセットして回転する
ソースとしてはこのような感じ
よさそうです
配置したいエリアはというと、距離的にフリースローからスリーポイントまでの間にあたる扇状の青の部分です
操作の都合上、リングの左右側の鋭角を10°程削っています
これをどう計算してランダムな座標を生成するか
もし対象とするエリアが長方形だったりすると話は簡単です
XとY軸の対象範囲をRandom.nextInt(value)するだけ
Rect area = new Rect(100, 100, 400, 300); Random random =new Random(); int x = area.left + random.nextInt(area.width()); int y = area.top + random.nextInt(area.height());試しに1000個ほど生成して描画してみましょう
Rect area = new Rect(100, 100, 400, 300);
canvas.drawRect(area, paint);
Random random =new Random();
for (int i = 0; i < 1000; i++) {
int x = area.left + random.nextInt(area.width());
int y = area.top + random.nextInt(area.height());
canvas.drawCircle(x, y, 3f, paint);
}
なんか今回は変形扇形のエリアではいろいろと方法はあると思いますが、このような考え方で生成しました
赤枠部分フリースローラインからスリーポイントラインまでの範囲でランダムにセットした座標を、対象範囲角をランダムにセットして回転する
ソースとしてはこのような感じ
Random random =new Random();
Plot ring = new Plot(7500, 1882);
for (int i = 0; i < 1000; i++) {
//リングX座標+フリースローラインまでの距離(4.118m)+フリースローラインからスリーポイントラインの間(2.52m)の間のランダム値
Plot base = new Plot(ring.x + 4118 + ((2520 * random.nextInt(100)) / 100), ring.y);
//削った10度+配置部分160°のランダム値
int angle = 10 + random.nextInt(160);
//回転処理
Plot pos = Utl.rotate(angle, ring, base);
//描画座標変換
pos = Court.getDPlot(pos);
canvas.drawCircle(pos.x, pos.y, 3f, paint);
}
試しに1000個ほど生成して描画よさそうです
2014年2月18日火曜日
新作Androidアプリ開発 ボールを操作で投げる編
投げたボールの処理が代替できたので、次にユーザーの操作でボールを投げるところを作ります
操作手法はこんな感じ
いわゆるAngryBird投射法です
処理の考え方としては引っ張り長さに制限をつけて、引っ張っていない時を0、最大まで引っ張った時を100として初速度を計算します

v0:初速度
d:引っ張り長さ
dm:最大引っ張り長さ
min:引っ張り0の時の速度
max:引っ張り100の時の速度
式は簡単ですが、minとmaxの値は初速度を決めるパラメータ、つまりはゲームバランスに繋がるのでテストと調整が非常に重要になります
あとは引っ張ったところからボール初期位置への角度を水平方向の投射角として
新作アプリ開発 ボールを投げてみる編で作ったshot関数
に投げてやれば完成
操作手法はこんな感じ
いわゆるAngryBird投射法です
処理の考え方としては引っ張り長さに制限をつけて、引っ張っていない時を0、最大まで引っ張った時を100として初速度を計算します
v0:初速度
d:引っ張り長さ
dm:最大引っ張り長さ
min:引っ張り0の時の速度
max:引っ張り100の時の速度
式は簡単ですが、minとmaxの値は初速度を決めるパラメータ、つまりはゲームバランスに繋がるのでテストと調整が非常に重要になります
あとは引っ張ったところからボール初期位置への角度を水平方向の投射角として
新作アプリ開発 ボールを投げてみる編で作ったshot関数
public void shot(double speed, double vangle, double hangle)に投げてやれば完成
2014年2月15日土曜日
新作Androidアプリ開発 ボールの跳ね返り リング編
前回ボールの跳ね返りをやりました
残るはリングとの衝突と跳ね返り処理です
これはかなり悩みました、3日ほど悩んでむりくりひねり出した結論ですが
※以下の考え方は物理的に正しいかどうかは不明です※
まず例としてこんなボールとリングの衝突と跳ね返りがあるとします
ごく当たり前の跳ね返りですが、これをどうプログラムで計算するのか?
とりあえず衝突点を求めます
そしてリングの断面の中心とボールの中心点を結んで衝突面の法線ベクトルを求めます
その衝突法線ベクトルが作る面に対しての3次元速度ベクトルの反射処理と考えました
この考え方を元にまずボールの移動前、移動後の位置を与えてリング当たっていれば衝突位置を返すメソッドを作成します
このメソッドをボールの移動処理にはさみます
試したところやや動きが怪しいところがありますが、基本方針としてはよさそう
残るはリングとの衝突と跳ね返り処理です
これはかなり悩みました、3日ほど悩んでむりくりひねり出した結論ですが
※以下の考え方は物理的に正しいかどうかは不明です※
まず例としてこんなボールとリングの衝突と跳ね返りがあるとします
ごく当たり前の跳ね返りですが、これをどうプログラムで計算するのか?
とりあえず衝突点を求めます
そしてリングの断面の中心とボールの中心点を結んで衝突面の法線ベクトルを求めます
その衝突法線ベクトルが作る面に対しての3次元速度ベクトルの反射処理と考えました
この考え方を元にまずボールの移動前、移動後の位置を与えてリング当たっていれば衝突位置を返すメソッドを作成します
//リングとの衝突判定関数
public static Plot hitRing(Plot prev, Plot move) {
Plot ret = null;
//大雑把な判定条件として落下方向への移動、移動のZ座標がリング高さをまたがっている
if (move.z < prev.z && move.z - Ball.R <= RING_H && prev.z - (Ball.R / 2) > RING_H) {
int r = (int)((((prev.z - Ball.R) - RING_H) * 100) / (prev.z - move.z));
//ボール下面がリング高さに来る位置取得
Plot pos = Utl.getPlotInLine(r, prev, move);
//リング中央との距離取得
int d = Utl.getDistancePtoP(RING_CENTER, new Plot(pos.x, pos.y));
//リングに当たりそうな距離にあればさらに詳しく判定
if (d > (RING_R - Ball.R) && d <= RING_R + Ball.R) {
float dagl = Utl.getRadians(Court.RING_CENTER, pos);
int dx = (int)(Court.RING_CENTER.x + Court.RING_R * (Math.cos(dagl)));
int dy = (int)(Court.RING_CENTER.y + Court.RING_R * (Math.sin(dagl)));
int bw = Utl.getDistancePtoP(new Plot(dx, dy), new Plot(pos.x, pos.y));
int bz = (int)(Ball.R * Math.sin(Math.acos((double)(Ball.R - bw) / (double)Ball.R)));
r = (int)(((prev.z - (pos.z - bz)) * 100) / (prev.z - move.z));
//実際にリングに当たる位置取得
pos = Utl.getPlotInLine(r, prev, move);
//リング中央との距離取得
d = Utl.getDistancePtoP(Court.RING_CENTER, new Plot(pos.x, pos.y));
//取得した距離からリングに当たるかどうか判定
if (d > (Court.RING_R - Ball.R) && d <= Court.RING_R + Ball.R) {
ret = new Plot(pos);
}
}
}
return ret;
}
こんな感じこのメソッドをボールの移動処理にはさみます
Plot hr = hitRing(prev, move);
if (hr != null) {
//法線ベクトル取得
float angle = Utl.getRadians(Court.RING_CENTER, hr);
int dx = (int)(Court.RING_CENTER.x + Court.RING_R * (Math.cos(angle)));
int dy = (int)(Court.RING_CENTER.y + Court.RING_R * (Math.sin(angle)));
//衝突時の速度ベクトルと法線ベクトルの内積を計算
float ip = Utl.getInnerProduct(new Plot(xSpeed, ySpeed, zSpeed), new Plot(hr.x - dx, hr.y - dy, hr.z - Court.RING_H));
//内積が+のときは裏からの当たりなので処理しない
if (ip < 0) {
//速度ベクトルの跳ね返り後の速度を取得
Plot dv = getReflect(new Plot(xSpeed, ySpeed, zSpeed), new Plot(hr.x - dx, hr.y - dy, hr.z - Court.RING_H));
float dk = 0.6f;
//反発係数dkを速度にかける
xSpeed = (int)(dv.x * dk);
ySpeed = (int)(dv.y * dk);
zSpeed = (int)(dv.z * dk);
//衝突後の経過時間を計算
int citv = (int)((interval * Utl.getDistancePtoP(hr, move)) / Utl.getDistancePtoP(prev, move));
//移動後の位置を変更速度から再計算
move.x = (int)(hr.x + ((xSpeed * citv) / 1000));
move.y = (int)(hr.y + ((ySpeed * citv) / 1000));
move.z = (int)(hr.z + ((zSpeed * citv) / 1000));
prev = new Plot(hr);
}
}
こんな感じ、いろいろと処理がひどいですが見ないでやってください(良い子はまねしちゃダメ)試したところやや動きが怪しいところがありますが、基本方針としてはよさそう
2014年2月12日水曜日
新作Androidアプリ開発 ボールの跳ね返り編
前回ボールを投げることが出来たので今回はバックボードとリングの接続部(フランジ)との跳ね返りの処理を作成したいと思います
処理の大まかな流れは以下
対象との衝突判定
↓
衝突位置を計算
↓
衝突後の速度を計算
↓
衝突後の位置を計算
まずはバックボードの部分、ボールの移動前、移動後の位置を与えて当たっていれば衝突位置を返すメソッドを作成します
このメソッドをボールの移動処理にはさみます
よさそうですね
次にフランジ部分、ほぼバックボードと一緒です
速度がY軸方向ではなくZ軸方向に反転します
よさそうです
衝突判定の仕方や衝突位置の取得方法はいろんなやり方があるので、それぞれのやりやすい方法でいいと思います
特にフランジの衝突位置計算なんかは強引に比率で出しちゃってますが、良い子はマネしちゃダメ
Rect.intersectsはRect同士の交差判定ができますので大雑把な判定するのに便利です
バックボードとフランジは軸に平行なため、跳ね返り処理は軸の法線方向への速度反転だけで済むので割と簡単ですね
あとゴールリング(リム)との跳ね返りがありますが、こちらは少し面倒なので次回にします
処理の大まかな流れは以下
対象との衝突判定
↓
衝突位置を計算
↓
衝突後の速度を計算
↓
衝突後の位置を計算
まずはバックボードの部分、ボールの移動前、移動後の位置を与えて当たっていれば衝突位置を返すメソッドを作成します
final private static int R = 122; //半径(mm)
final private static int BOARD_Y = 1400;
final private static Plot BOARD_LEFT = new Plot(6600, BOARD_Y + R);
final private static Plot BOARD_RIGHT = new Plot(8400, BOARD_Y + R);
final private static Rect BOARD_H = new Rect(6600 - R, 0, 8400 + R, 10);
final private static Rect BOARD_V = new Rect(0, 2900, 10, 3950);
//バックボートとの衝突判定関数
public Plot hitBoard(Plot prev, Plot move) {
Plot ret = null;
//大雑把な判定条件としてボード方向へ移動している、移動のY座標がボード位置をまたがっている
if (move.y < prev.y && (move.y - Ball.R <= BOARD_Y && prev.y + Ball.R > BOARD_Y)) {
//移動前後のZ座標にボールの半径を加味したRectを生成
Rect rv = new Rect(0, (prev.z < move.z ? prev.z : move.z) - Ball.R, 10, (prev.z < move.z ? move.z : prev.z) + Ball.R);
//移動前後のX座標にボールの半径を加味したRectを生成
Rect rh = new Rect(prev.x < move.x ? prev.x : move.x, 0, prev.x < move.x ? move.x : prev.x, 10);
//ボードの垂直方向Rect(BOARD_V)と水平方向Rect(BOARD_H)と生成したrv、rhを交差判定する
if (Rect.intersects(BOARD_V, rv) && Rect.intersects(BOARD_H, rh)) {
//移動直線とボードから水平方向交点を取得(getLineVSLineは2直線の交点を返す関数)
ret = Utl.getLineVSLine(new Plot(prev.x, prev.y), new Plot(move.x, move.y), BOARD_LEFT, BOARD_RIGHT);
}
}
//衝突がなければnullをあれば衝突位置を返す
return ret;
}
こんな感じこのメソッドをボールの移動処理にはさみます
final private float bk = 0.95f;
//位置変更
Plot prev = new Plot(position);
Plot move = new Plot(position);
move.x += ((xSpeed * interval) / 1000);
move.y += ((ySpeed * interval) / 1000);
move.z += ((zSpeed * interval) / 1000);
Plot hb = hitBoard(prev, move);
if (hb != null) {
//反発係数bkを速度にかける
xSpeed *= bk;
ySpeed *= -bk; //y方向の速度を反転
zSpeed *= bk;
//衝突後の経過時間を計算
int citv = (int)((interval * Utl.getDistancePtoP(hb, move)) / Utl.getDistancePtoP(prev, move));
hb.z = (int)(prev.z + ((zSpeed * (interval - citv)) / 1000));
prev = new Plot(hb);
//移動後の位置を変更速度から再計算
move.x = (int)(hb.x + ((xSpeed * citv) / 1000));
move.y = (int)(hb.y + ((ySpeed * citv) / 1000));
move.z = (int)(hb.z + ((zSpeed * citv) / 1000));
}
こんな感じ、これを動かしてみますよさそうですね
次にフランジ部分、ほぼバックボードと一緒です
速度がY軸方向ではなくZ軸方向に反転します
final private float fk = 0.92f;
Plot hf = hitFlange(prev, move);
if (hf != null) {
//反発係数fkを速度にかける
xSpeed *= fk;
ySpeed *= fk;
zSpeed *= -fk; //z方向の速度を反転
prev = new Plot(hf);
//衝突後の経過時間を計算
int citv = (int)((interval * Utl.getDistancePtoP(hf, move)) / Utl.getDistancePtoP(prev, move));
//移動後の位置を変更速度から再計算
move.x = (int)(hf.x + ((xSpeed * citv) / 1000));
move.y = (int)(hf.y + ((ySpeed * citv) / 1000));
move.z = (int)(hf.z + ((zSpeed * citv) / 1000));
}
//フランジとの衝突判定関数
public Plot hitFlange(Plot prev, Plot move) {
Plot ret = null;
//大雑把な判定条件として落下方向へ移動している、移動のZ座標がフランジ高さをまたがっている
if (move.z < prev.z && move.z - Ball.R <= FLANGE_H && prev.z - Ball.R > FLANGE_H) {
//フランジ高さに達するまでの比率を計算
int r = (int)((((prev.z - Ball.R) - FLANGE_H) * 100) / (prev.z - move.z));
//フランジ高さに達した時の位置を取得
Plot pos = Utl.getPlotInLine(r, prev, move);
//フランジとの交差判定
if (FLANGE.contains(pos.x, pos.y)) {
//フランジとの衝突位置をセット
ret = new Plot(pos.x, pos.y, FLANGE_H + Ball.R);
}
}
//衝突がなければnullをあれば衝突位置を返す
return ret;
}
動かしてみますよさそうです
衝突判定の仕方や衝突位置の取得方法はいろんなやり方があるので、それぞれのやりやすい方法でいいと思います
特にフランジの衝突位置計算なんかは強引に比率で出しちゃってますが、良い子はマネしちゃダメ
Rect.intersectsはRect同士の交差判定ができますので大雑把な判定するのに便利です
バックボードとフランジは軸に平行なため、跳ね返り処理は軸の法線方向への速度反転だけで済むので割と簡単ですね
あとゴールリング(リム)との跳ね返りがありますが、こちらは少し面倒なので次回にします
2014年2月6日木曜日
新作Androidアプリ開発 ボールを投げてみる編
新作アプリはバスケシュートゲームなんで何はともあれボールを投げてみましょう
ボールを投げるということでまずBall.classの作成
必要となる主要メンバはボールの速度、位置ですね
ここで位置を表すpositionのクラスPlotはxyz座標を持った自作クラスです
これに初速度と垂直・水平方向の投射角を与えてやります
まず与えた初速度をMath.sin(垂直角度)cos(垂直角度)で水平方向と垂直方向の速度に分解
分解した水平方向の速度をまたMath.sin,cos(水平角度)でXY方向の速度に分解してやります
XYZに分解してやれば、それぞれに経過時間をかけるだけで移動距離が出ます
経過時間intervalはミリ秒、物理で習ったように重力加速度をZ方向速度に加算します
移動距離は速度×時間ですからXYZ方向の各速度に(interval/1000ミリ秒)かけた分を加算すれば移動後の位置が取得できます
計算式としては

となりますが、そのままコードにして
こういった最終的にint型が必要だとしても、途中計算までもintで行うことはお勧めしません
このように必ず分子に比率をかけてから除算しなければなりません
こんな感じ
投げた感じがよくわからないので、横からの様子を描画
よさそうですが、なんか飛びすぎな感じがします
学校の物理では省略されましたが、ボールの投射の実際には空気抵抗による影響があります
そこで空気抵抗の計算方法を調べてみますとこのような式が出てきました
参考:ホームランの打球について
http://www.nasekouki.co.jp/~shig/homerun.html

Cd:抗力係数
p:空気密度
v:速度
A:ボールの見付け面積
速度によって変わるようですね、回転速度なども影響するようです
計算がめんどくさい大変なのでやめます
本格的なシミュレータを目指してるわけではないので、簡易式を作ります

k:空気抵抗簡易係数
これを取り入れてmove関数を修正
これを実行してみます
それらしくなったので良しとします
ボールを投げるということでまずBall.classの作成
public class Ball {
final public static int G = 9800; //重力加速度(mm/s)
final public static float k = 0.01f; //空気抵抗係数
final public static int R = 122; //半径(mm)
public Plot position = new Plot(); //位置
public int xSpeed; //X方向の速度(mm/s)
public int ySpeed; //Y方向の速度(mm/s)
public int zSpeed; //Z方向の速度(mm/s)
public Ball() {}
}
必要となる主要メンバはボールの速度、位置ですね
ここで位置を表すpositionのクラスPlotはxyz座標を持った自作クラスです
これに初速度と垂直・水平方向の投射角を与えてやります
public void shot(double speed, double vangle, double hangle) {
//speed 初速(mm/s)
//vangle 垂直方向の角度
//hangle 水平方向の角度
zSpeed = (int)(speed * Math.sin(vangle));
speed = speed * Math.cos(vangle);
xSpeed = (int)(speed * Math.cos(hangle));
ySpeed = (int)(speed * Math.sin(hangle));
position = new Plot(6423, 8682, 1800);
}
まず与えた初速度をMath.sin(垂直角度)cos(垂直角度)で水平方向と垂直方向の速度に分解
分解した水平方向の速度をまたMath.sin,cos(水平角度)でXY方向の速度に分解してやります
XYZに分解してやれば、それぞれに経過時間をかけるだけで移動距離が出ます
public void move(long interval) {
//重力加速度による加速
zSpeed -= ((G * interval) / 1000);
//位置変更
position.x += ((xSpeed * interval) / 1000);
position.y += ((ySpeed * interval) / 1000);
position.z += ((zSpeed * interval) / 1000);
}
経過時間intervalはミリ秒、物理で習ったように重力加速度をZ方向速度に加算します
移動距離は速度×時間ですからXYZ方向の各速度に(interval/1000ミリ秒)かけた分を加算すれば移動後の位置が取得できます
計算式としては
となりますが、そのままコードにして
int x = x + (vx * (interval / 1000));などとしてしまうと式に使われている変数はintとlongしかありませんので(interval / 1000)の部分は整数に丸められてしまいます
こういった最終的にint型が必要だとしても、途中計算までもintで行うことはお勧めしません
このように必ず分子に比率をかけてから除算しなければなりません
int x = x + ((vx * interval) / 1000);
こんな感じ
投げた感じがよくわからないので、横からの様子を描画
よさそうですが、なんか飛びすぎな感じがします
学校の物理では省略されましたが、ボールの投射の実際には空気抵抗による影響があります
そこで空気抵抗の計算方法を調べてみますとこのような式が出てきました
参考:ホームランの打球について
http://www.nasekouki.co.jp/~shig/homerun.html
Cd:抗力係数
p:空気密度
v:速度
A:ボールの見付け面積
速度によって変わるようですね、回転速度なども影響するようです
計算が
本格的なシミュレータを目指してるわけではないので、簡易式を作ります
k:空気抵抗簡易係数
これを取り入れてmove関数を修正
final public static float k = 0.01f; //空気抵抗係数
public void move(long interval) {
//空気抵抗による減速
xSpeed -= ((xSpeed * k * interval) / 1000);
ySpeed -= ((ySpeed * k * interval) / 1000);
zSpeed -= ((zSpeed * k * interval) / 1000);
//重力加速度による加速
zSpeed -= ((G * interval) / 1000);
//位置変更
position.x += ((xSpeed * interval) / 1000);
position.y += ((ySpeed * interval) / 1000);
position.z += ((zSpeed * interval) / 1000);
}
これを実行してみます
それらしくなったので良しとします
登録:
投稿 (Atom)


























