2013年3月21日木曜日

Unity基本メソッド覚書

Unityでいろいろ作っている時に,
「あれってどう実装するんだっけ…」「これ動かすときどこの値をいじるんだっけ…」
なんてことが最近多かったのでメモメモφ(・ω・ )

Unityでスクリプトを書くとき覚えておきたいメソッド-移動と回転-


0. 基本

移動にしても回転にしても, 座標を用いてパラメータをいじるのは基本
var myTransform : Transform = this.gameObject.transform;
ここ.
Hierarchyウィンドウから適当なオブジェクトを選択して, Inspectorを眺めると必ずあるのがこの transform.
位置, 回転, スケールを管理するここはスクリプトからもよくアクセスされる.
スクリプト中で何度もアクセスするようなら, 上記のように変数として持ってしまうのも, 処理の高速化として有効らしい.
ちなみにC#で書くなら, 
Transform myTransform = this.gameObject.transform;
ほぼ変わらない.

1. 移動

ゲーム作成において必須項目. なので「プレイヤーを歩かせる」「足場を動かす」「弾を打ち出す」など, いろいろなところで使う. ちなみにC#でもUnityScriptでもほとんどコードは変わらず. おかげでC#のお勉強にならない…(・ω・`)

・ positionをいじる

myTransform.position = new Vector3( x, y, z);
Vector3型で指定した x, y, zの座標に「ワープ」する.
なので「歩く」モーションなどには向かない.
ヨ○テレ○ートみたいなワープ移動や, ロック○ンの"消える足場"みたいなものなら使えるかもしれないが, 基本移動を表現するには不向き.

・ Translate

myTransform.Translate(Vector3 vec);
Vector3型で指定したベクトル方向に「動かす」メソッド. プレイヤーを歩かせるならこれか.
コード中で使うなら,
var speed: float;
myTransform.Translate(Vector3 vec * speed * Time.deltaTime)
となるかと. Vector3で方向を, speedで移動速度を設定. Time.deltaTimeで環境ごとのフレームレートの差を補完させる. 処理がマシンによってばらばらになるのが嫌なら, つけておくと安心できるかと.
参考

・addForce

これだけちょっと毛色が違う. こちらは
this.gameObject.rigidbody.addForce(Vector3 vec);
Vector3型で宣言した方向に力を加えるメソッド. まずアクセスするのが Transformではなく Rigidbody. これは「Unityが面倒な物理演算を自分でやってくれる」ための重要なコンポーネント. ここにアクセスして, 「力を加えて」動かす.
要するに「物を殴って動かす」ようなもの. このメソッドを呼んだ一瞬は力がかかるけど, 以降は慣性で移動する.
おもに弾丸の発射やピンボールのバンパーなんかに用いると幸せになれるかも.
当然対象に rigidBodyがないとだめ. addしておきましょう.

2. 回転

同じく必須項目. たとえ横スクロールアクションであろうと左右の向きくらいは変えるはず. 「向きを変える」「ターゲットの方向を向く」「軸を合わせる」なんかでいろいろ使う.

・LookAt

var targetTransform: Transform;
myTransform.LookAt(targetTransform);
Transformから直接呼び出すメソッド. シンプルに
「targetTransformに指定したものの方を見る」
というもの.
一見便利なのだが, 問題は myTransformと targetTransformが接近したとき.
「近づく」まではぎりぎり行けるかいけないかはわかれるが, 接触してしまうとかなり猛烈な荒ぶり方をしてしまう.
またこの「見る」は, 全身の正面を targetTransformに向けるので, 両者のサイズ差が大きいと, 見上げる or 見下ろす ような体制になりやすいのが難点.

逆に接近することがないようなもの, 侵入者を照らすサーチライトの光源なんかはこれで十分かと.

・rotation- 1

myTransform.rotation = Quaternion.Euler( x, y, z);
transformのrotationに, オイラー角で指定して角度を入れる. この Quaternionクラスが角度の操作をする時に便利.特に,
myTransform.rotation = Quaternion.AngleAxis( float angle, Vector3( x, y, z));
と, AngleAxisメソッドを使うと便利. これは第一引数に角度を, 第二引数に回転軸を設定して回転させる, というもの. Update()メソッドの中で徐々に角度の部分を増やしてやれば, ゆっくり回転する物体の出来上がり. 変に角度いじるよりわかりやすいかと.
またこれは掛け合わせることで, 複数の軸へのそれぞれの回転量を表すこともできる.
たとえば, 「x軸を軸に45度, y軸を軸に120度回転させた状態」にしたいなら,
myTransform.rotation = Quaternion.AngleAxis( 45, Vector3( 1, 0, 0))
                       * Quaternion.AngleAxis( 120, Vector3( 0, 1, 0));
と, それぞれの条件を書いた式をかけてやるだけでいい. これはわかりやすい.
ただし順序があり, 最後にかけた回転から順に行われるらしく, この場合は
「 y軸を軸に120度回転させてから, x軸を軸に45度回転させる」という順序で回転が行われる. 結果が同じならいいけれども….
たとえば

こんな棒があった時,

上記の y軸 → x軸回転時
その逆 x軸 → y軸回転時
とここまで変わるので注意が必要.

主に回転し続ける足場やカメラなど, 常に角度が変化するものに強い?

・rotation - 2

myTransform.rotation = Quaternion.LookRotation(myTransform.position - targetTransform.position);

いじる場所は一個前と同じ myTransform, 使うクラスも Quaternionとこれまた同じ.
やることは二個前と同じ, 「自分を targetTransformの方に向かせる」 というもの.
では何が違うのかというと, 引数に Vector3クラスが使える という点だろう.
LookAtの際問題になるのが, 両者が接近したとき, と紹介したが, これを回避できるのがコイツ.
やり方は
var vec: Vector3 = myTransform.position - targettransform.position;
vec.y = 0;
myTransform.rotation = Quaternion.LookRotation(vec);
としてやる. 要は両者の y軸(高さ)の差を0としてやれば, 変に上下に傾くことがなくなる.
応用してやれば, myTransformの y = 0の場所から見た方向を向く, などもいじりやすいのが利点. 
2D視点など軸が一ついらないゲームなんかではこっちの方が変な動きをしなくて安心かと. 

・Rotate

myTransform.Rotate( x, y, z);
//or
myTransform.Rotate(Vector3 vec);
こちらは x, y, z の角度をそれぞれ指定して回すメソッド. 角度が完璧に決まっているならこちらのが早い. がやはり徐々に変化させるタイプには向かない.
スポーン時に最初の向きを決めてやったり, マ○オのような2Dスクロールで進行方向を変える時などはこっち?.

最近の○リオはちゃんと振り向きモーションあるけどね(・ω・`).


・おまけ

myTransform.rotation = Quaternion.identity;
回転なしの状態にする = すべての角度を 0 にする.
角度の初期化などに便利?





-移動と回転-とか書いたけどこれは他にもまとめる時がくるフラグなんだろうか…(・ω・`)

2013年3月15日金曜日

Wait.until()で指定できる状態の種類

Selenium2でテストを書いていく際, 何かとお世話になるのが Wait<WebDriver>.until(). 「引数に指定した状態になるまで」待機してくれるのだが, この状態の指定のメソッドが多すぎる上に, いちいち英語読むのがダルげふんげふん説明が単調なのでまとめてメモメモφ(・ω・ )
ちなみに 「何言ってんだコイツ?(・д・`)」と感じられたら, こちらの本家へジャンプ.
またこちらでメソッドの中身を見るのもいいかも.

Wait.until()の指定できる「状態」


1. 準備


・インポートと宣言

import org.openqa.selenium.support.ui.Wait;
import static org.openqa.selenium.support.ui.ExpectedConditions.*;

WebDriver driver = new ChromeDriver();
Wait<webdriver> wait = new WebDriverWait(driver, 10);
まずは準備. newする際に, 待機を指示する WebDriverの指定と, 最大待機時間を宣言. 今回は 10秒を指定. もちろん 10を 20に置き換えれば最大 20秒待機となる. この待機時間を過ぎると,
org.openqa.selenium.TimeoutException
が発生し, テストはそこで failとなる.
逆にこの最大待機時間内に conditionが整えば, わざわざ最大待機時間まで待機せずに先に進んでくれる.
Thread.sleep(10000);
とかするよりよっぽどスマート.


・until

wait.until(ExpectedCondition condition);
wait.until()を呼び出す. この引数に以下に紹介する conditionを設定して渡してやればOK.
たとえば titlesで状態を設定したいなら,
wait.until(titles("期待するタイトル"));
としてやる.
使い方自体はシンプルなのだが, 問題はこの「期待する状態の設定」の方になる.


2. conditionの設定

・titles

public static ExpectedCondition<java.lang.Boolean> titleIs(java.lang.String title)
「ページのタイトルが titleに一致するまで」待機.
・titleContains

public static ExpectedCondition<java.lang.Boolean> titleContains(java.lang.String title)
「ページのタイトルに titleが含まれるまで」待機.
・presenceOfElementLocated

public static ExpectedCondition<WebElement> presenceOfElementLocated(By locator)
「引数に指定した locatorがDOMに現れるまで」待機. ただし待機終了時に, 必ずしもそれが画面に表示されているとは限らない.
・visibilityOfElementLocated

public static ExpectedCondition<WebElement> visibilityOfElementLocated(By locator)
↑に近く, 「引数に指定した locatorがDOMに現れるまで」待機.こちらは画面に表示されたうえで, それが0より大きい幅高さを持って存在している状態になるまで待機する.
・visibilityOf

public static ExpectedCondition<WebElement> visibilityOf(WebElement element)
visibilityOfElementLocatedに近いが, こちらは「引数に指定した elementがDOMに現れるまで」待機. ”画面に表示される”の定義も visibilityOfElementLocatedと同じ.
・presenceOfAllElementsLocatedBy

public static ExpectedCondition<java.util.List<WebElement>> presenceOfAllElementsLocatedBy(By locator)
「引数に指定した locatorが, ページ上に少なくとも一つ存在するまで」待機. 見えるかどうかは問わないらしい.
・textToBePresentInElement

public static ExpectedCondition<java.lang.Boolean> textToBePresentInElement(By locator, java.lang.String text)
「引数に指定した textが, 指定した locatorに textとして現れるまで」待機. 要するに指定した locatorでgetText()したら textがとれるようになるまで待機?
・textToBePresentInElementValue

public static ExpectedCondition<java.lang.Boolean> textToBePresentInElementValue(By locator, java.lang.String text)
↑に近いが, こちらは「引数に指定した textが, 指定した locatorの valueとして現れるまで」待機.
・frameToBeAvailableAndSwitchToIt

public static ExpectedCondition<WebDriver> frameToBeAvailableAndSwitchToIt(java.lang.String frameLocator)
「指定した frameLocatorに WebDriverが移動するまで」待機?そもそもframeがよくわかってないし, ここを見た分には, このメソッドでそのまま移動しそうな switchTo()が呼ばれてるけどどういうことなんだろう…
・invisibilityOfElementLocated

public static ExpectedCondition<java.lang.Boolean> invisibilityOfElementLocated(By locator)
今度は「引数に指定した locatorが, 見えなくなるかDOM上からいなくなるまで」待機. "見えなくなる"の定義はないのかな…(・ω・`)
・invisibilityOfElementWithText

public static ExpectedCondition<java.lang.Boolean> invisibilityOfElementWithText(By locator, java.lang.String text)
「 textで指定した要素が, locatorで指定したエレメントから, 見えなくなるかDOM上からいなくなるまで」待機, …でいいのかな.
・elementToBeClickable

public static ExpectedCondition<WebElement> elementToBeClickable(By locator)
「 locatorで指定したエレメントが, 画面上に表示され, かつクリックできる状態になる」まで待機.
・stalenessOf

public static ExpectedCondition<java.lang.Boolean> stalenessOf(WebElement element)
「 elementがDOMに付属しなくなるまで」待機. ページのリロードなどでDOMが置き換わるなどするときに, "指定した elementがDOMからちゃんと消えるまで"待つ, ってこと? 参考
refreshed

public static <T> ExpectedCondition<T> refreshed(ExpectedCondition<T> condition)
このメソッド…無くなってる…?まぁ正直どういう動きか微妙に想像つかないけど(・ω・`)
・elementToBeSelected

public static ExpectedCondition<java.lang.Boolean> elementToBeSelected(WebElement element)
「指定した elementが選択された状態になるまで」待機. チェックボックスとかに使うんだろうけど, どこで判断してるんだろう…
・elementSelectionStateToBe

public static ExpectedCondition<java.lang.Boolean> elementSelectionStateToBe(WebElement element, boolean selected)
ほぼ↑と同じ気が…. 「指定した elementが selectedで指定した状態に選択 された/されていない 状態になるまで」待機. こっちで統一してもいい気がする…. 説明文とかコピペを疑うし…
・elementToBeSelected

public static ExpectedCondition<java.lang.Boolean> elementToBeSelected(By locator)
↑の locator版?「 locatorで指定したエレメントが選択された状態になるまで」待機, 当たりか. 説明すら放棄されましたorz
・elementSelectionStateToBe

public static ExpectedCondition<java.lang.Boolean> elementSelectionStateToBe(By locator, boolean selected)
「 locatorで指定したエレメントの選択状態が, selectedで指定した状態になるまで」待機, でいいのかな? もはや説明不要といわんばかりの勢い
・alertIsPresent

public static ExpectedCondition<Alert> alertIsPresent()
おそらく「アラートが出るまで」待機. 見たまんまでも説明がないと不安になりますよ…('A`)
not

public static ExpectedCondition<java.lang.Boolean> not(ExpectedCondition<?> condition)
シンプルに否定. 「引数に指定した conditionの notの状態になるまで」待機. …ってこのメソッドも無くなってるし( ゚д゚ )使えそうとか思ったのに…(;ω;`)


とりあえず以上. なんか無くなってるのか違うとこ見てるのかわからないのが2つほどあったけど…






枠とか試してみたけど…レイアウトって難しい(・ω・`)

2013年3月13日水曜日

プルダウンメニューをWebDriverに選んでもらう

いわゆる

こーいうの
これをWebDriverに選んでもらいたいけどちょっと詰まったのでメモメモφ(・ω・ )

プルダウンメニューをWebDriverで選択する

たとえば, 
<select name="preview">
    <option value="0">おはよう</option>
    <option value="1">こんにちわ</option>
    <option value="2">こんばんわ</option>
    <option value="3">おやすみ</option>
</select>
こんな <Select>があったとすると
ただの Select- optionのプルダウンメニューならサポートしてくれているらしい.
Select selector = new Selector(driver.findElement(By.name("preview")));
これで準備完了. 試しに 「こんにちわ」 を選びたいなら,
selector.selectByValue("1");
selector.selectByIndex(1);
selector.selectByVisibleText("こんにちわ");
など, optionの value, id, 表示テキストで選択できる親切設計.

…ってこここことかここを参考にしてみたけれど…(・д・`)できない

なんでだろうとみてみると
Element should have been "select" but was "input"
なんて警告.
なんか 「inputよこしてるぞゴルァ!щ(゚Д゚#щ) =3」的な話?

こことか眺めていると, どうやらカスタムしたような<select>には対応していないみたいな空気.
…べつにカスタムしてないと思うんだけどなぁ…(・ω・`)

とか思ってたら原因は
nameで locaterを指定していたら, <select>ではなく別の<input>を指定していた
模様. くだらなすぎる…orz
今一度 classや nameに被りがないかちゃんと確認を…(・ω・`)

ちなみにできないと思って探してみた違うやり方.
WebElement selector = driver.findElement(By.name("preview"));
selector.findElement(By.xpath("//option[@value='1']")).click();
いちおうこれでも選択はできた. できたけど…('A`)
まぁ 最後の手段 的なやり方ってことで…





というかプルダウンメニューがここに貼れたことにびっくり( ゚д゚ )

WebElementをもっと楽に探したい

テストを作っているとよく出てくる問題が
「同じようなタグの要素がいっぱいある…」「どう1個だけを指定したらいいんだ…」
なんて状態. リストが複数あるなかその中の要素を1個だけ選べとか言われても下手するとBy.xpath()使っても20や30も Listになって出てきたり, そもそもリスト以外の要素が引っかかったり…
そんな時だいぶ楽になる要素の探し方.

子要素の探索

要するにリストのように全く同じ要素が複数作成されるときは, 親要素に入れて管理している場合がほとんど(だと思う(・ω・`)). ならば親要素を特定してからその中を探索してやればすっごい楽になるよね!というお話. まぁ単純な話です. ちなみにコード中に出てくる Byについてはこちらにもメモがございます(宣伝
たとえば, 
<div class="parent1">
  <span class="child"></span>
  <span class="child"></span>
</div>
<div class="parent2">
  <span class="child"></span>
  <span class="child"></span>
  <span class="child"></span>
</div>
なんて要素の階層があったとする.
2つ目の divの中の2つ目の spanを探したい時, 直接探そうとするとかなりめんどくさい.
なので,
WebElement parent = driver.findElement(By.className("parent2"));
WebElement child = parent.findElemens(By.className("child")).get(1);
と, 先に親エレメントを特定してから, その下のエレメントを探してやることができる. コード的にだいぶ意味が通りやすくなるはず. なおもちろん driverは WebDriver.
これを「class="child"だけで見れば 4番目だから Listで取得して .get(3)だ!」なんて考えて
WebElement child = driver.findElement(By.className("child")).get(3);
こんなコードを書くとまず他の人には伝わらない. まして上の divに要素増えた時には…
Σ(||゚Д゚)ヒィィィィ

また, まとめて
WebElement child = driver.findElement(new ByChained(By.className("parent2"),By.className("child"))).get(1);
なんていう書き方も. これは
new ByChained(By locator, …)
を用いることで, 順に探している(らしい).


ただ問題はここまでのこと,  By.xpath()を使うとなんかうまくいかない…(・ω・`)なんでだろ







一番楽かつ安定なのは classなりわかりやすくつけて指定してやることなのよね…

2013年3月11日月曜日

見回りをする敵の作成

よくある雑魚敵.
見回りの兵士しかり, ただ歩く雑魚しかり, よくある敵の動きとして決められたポイントを動く敵の動きを組んでみた.
作った段階順にメモメモφ(・ω・ )

見回り兵の動きの作成

1. ターゲットの方を「向く」

今回考えたのが, ターゲットの方を向く → 前進! でシンプルにいけるんじゃね?ということ.
なのでまずは「ターゲット」としたものの方を向くことから
var speed : int;
function walkToTarget(t:Transform){
 var targetVec : Vector3 = t.position;
 targetVec.y = this.gameObject.transform.position.y;
 gameObject.transform.LookAt(targetVec);
 this.transform.Translate(0,0,1 * speed * Time.deltaTime);
}
6行目はまだ見ないで('A`). 引数の tは目標の transform. speedはお好みで. ここに出しておくと Unityの Inspectorですぐいじれるから便利.
3行目で目標の positionを取得. これをtransform.LookAt()に放り込むだけで, 引数に入れた positionの方を向いてくれるから便利. ただしこのままだと, 目標が自分より高い位置にいる場合, 最悪仰向けになるという間抜けさ.
なので 4行目, 見る positionの高さ yを自分の高さにしてやれば, 不自然に仰向けうつ伏せになることはなくなる.
これをUpdate()のなかで呼んで, とりあえず目標の方に「向く」ことができた.

2. 「前進」する. 

わりと大事だと思ってる. 適当な区間を歩かせようとしたとき, 「ここから x歩いたら y度修正して 半径 rの弧のうえを歩くように…」なんて考えたくない('A`) できる限り楽したいのです.
this.transform.Translate(0,0,1 * speed * Time.deltaTime);
さっき見えちゃったけど. transform.Transrate()の引数に Vector3で動く量を指定してやるだけ.
今回はただ前進したいので zだけ増やして残りは 0. speedは1. の通りお好み. Time.deltaTimeはフレームレートの違いを吸収して, 単位時間当たりの変化量を一定にしてくれるらしい. 端末の違いなどで変化が出ないようにするのに非常に役立つ.

ここまでで, あとは適当なところで目標を指定してやれば, そいつを追い続ける動きができる. がそれだと巡回ができない.
なので「巡回のポイントごとにマーカーを置き, そこに到達したら次の目標を指示する」ようにしてやる.

3. マーカーの作成

イメージはカラーコーンか. こいつを作る前に, 1. 2. を載せる敵に Enemyのタグをつける. これで「巡回中の敵がマーカーにたどり着いた」を識別する.
まずはマーカーの GameObjectの作成. これは何でもいいけど, 必ず敵が「到達」できるようにすること. はるか上空にあるマーカーを眺めて荒ぶる兵士とかかわいそうすぎる(;ω;`)
適当に cube当たりをチョイス. 名前を checkPoint1とでもしたら Inspectorを見る.
見えているマーカーを回るならともかく, 基本は何もないだろうし, Mesh Filterの Meshを Noneに. 
Colliderは Is Triggerをチェックしておく. 
こうすることで「トリガー」となり, 物理的な当たり判定が消え, 物が素通りするようになる. 
ただし「衝突した」ことはとれるので, センサーなど目に見えない範囲を作るときに重宝する. 
今回もその恩恵にあやかる. 
準備ができたらスクリプト.
var nextTarget : GameObject;

function OnTriggerEnter(object:Collider){
 if (object.gameObject.tag == "Enemy"){
  object.gameObject.SendMessage("changeTarget",nextTarget);
 }
}
OnTriggerEnter()は引数の「なにか」がトリガーに引っかかったぞ, というメソッド. 何がトリガーの範囲に入ってきても呼ばれる. なのでふるい分けをしてやる.
4行目は, 入ってきた「なにか」のタグを取得し, if文でふるい分ける. 今回は敵が巡回してきたら反応したいので, タグは Enemy.
Enemyが来たらそいつに対し, 5行目の処理. 内容はぶつかってきた objectに対して, 「changeTargetというメソッドを, nextTargetという引数を与えて呼び出せ」ということ. これまた便利.
あとは nextTargetに次に目指してほしいマーカーを入れてやるといい.

4. 目標切り替えの処理

これは単純
var target : GameObject;
function changeTarget(newTarget:GameObject){
 target = newTarget; 
}
これだけ. これを敵の方のスクリプトに持たせる. マーカーではない. このメソッドを持ってマーカーに突っ込むと, 次の目標の支持が飛んでくる寸法.
最後に Inspectorから, 「一番最初に向かう目標のマーカー」を敵の scriptの targetのところに指定してやればいい.

ちなみにこの指定の際,  targetをフィールドに宣言していると, Hierarchyに並んでいるマーカーを, Inspectorの scriptの中に表示されている targetにドラッグ&ドロップしてやるだけでいい.
どこを指定したかがわかりやすいかと.

5. マーカーをばらまく

マーカーをコピーしてばらまくだけ. もちろん「次の目標」はそれぞれに設定しないといけないけど.

これで「適当なポイントを巡回する兵士」みたいな動きができる.
ただこのままだと怪しいやつが目の前を通っても無視するおバカ兵士になってしまう.
のでその辺を足すとより敵っぽくなるかと.




ながなが書いたけどコードにするとめっさ短いのがUnityのいいところ. ∩( ・ω・)∩




2013年3月4日月曜日

RECTの勘違い

ずっと勘違いしたまま何とかやってこれてしまったので
ここらで矯正

Rect()の範囲指定

特にGUIいじるときなんかに登場する, 四角形の範囲を宣言するクラス(?). 
Rect(x,y,width,height)
中の4つの引数で範囲を宣言するのだが, 何と混ざったか4つの引数を

左上(x0,y0)と右下(x1,y1)ととった四角形の2点

Rect(x0,y0,x1,y1)

でとるものとか勘違い.
正解は

Rect(x0,y0,x1-x0,y1-y0)

左上の1点と幅高さで宣言するものとのこと.




どうしてこうなった…(・ω・`)



AWS CDKで立てたEC2インスタンスのTimeZoneとかいじりたかった話

EC2を立てることはできたけど、立てたインスタンスは UTCのままだし設定ファイルとかいちいちscpしてくるのはダルい。 当初UserDataでなんとかしようとしたものの、「書く量がヤバいしメンテしにくい」と悩んでいたところ見かけたのが  AWS::CloudFormation:...