RailsのSelectタグでenumを使うかつi18nを利用する
いつも忘れるのでメモ。
model
class User < ApplicationRecord enum state: { unpublished: 0, published: 1 } end
i18n file
ja: activerecord: attributes: user: email: メールアドレス password: パスワード enum: user: state: unpublished: 非公開 published: 公開
view
= f.select :state, User.states.keys.map{|key| [I18n.t("activerecord.enum.user.state.#{key}"), key] }, {}, class: "select-css"
雑にやるならこんな感じ。
テストについて
E2Eテスト
今日ちょっとE2Eテストについて説明してみてって上司から言われ説明してみたら0点いただきました。Wow...ちゃんと勉強しよ。
インテグレーションテストが間違ってたっぽい。
E2Eのテストとインテグレーションテストがごちゃっとしていて、それじゃおかしくなるとの指摘。なんとなくだけど久しぶりに詰められた気がする。
詰められるの嫌だけど、だめなところをしっかり痛いところをついて指摘してくれるのはすごくありがたい。
「3年目でこれはやばくないか?」
まじでそれ。やればやるほどコンピュータの世界知ることが多い。
一つづつ着実に身に着けて行かないと上辺だけでやってたら死ぬと最近思っていた矢先だったので、いい機会だったと思う。
- Unitテストはクラス単体のテスト
- Integrationテストはクラス同士の結合テスト
- E2Eはユーザーケースを自動化したテスト
じゃあRailsのModelの単体テストはUnitテストなのか?という議題に関して、概念的にはNoらしい。
しかし事実上はYesとなっていて、そもそもRailsのModelはActiveRecordを継承しているので、そもそもUnitテストじゃなくなる。
でもそれをUnitテストってことにしちゃおう、みたいな流れ。
Unitテストをガチガチにやるなら別クラスのものはMockとStubを使ってテストする。
Railsだと厳しい感じある。
Controllerテストはどうか、あれはIntegrationの役割をしているが、render_viewsを時折利用していて、Viewと密になっているケースがある。
まぁなにわともあれ、Unitテスト、Integrationテスト、そしてE2Eのテストを書こうね、そしてそれぞれが持つ役割をちゃんと理解しておこうねという話。
RailsだとE2EはFeatureSpecか、SystemSpec。
最近ChromeDriverが使えるようになって前よりも安定してきている気がしている。
最後に、もう一度ちゃんと初めての自動テストを読むことにしてPDFを購入して、1章を読み終えたところだ。
npm first release
npmのpackageをリリースした。その名も rcolor.js
。
最近ツヨツヨなエンジニアはみんなライブラリを公開しているっぽいので、ツヨツヨを目指すべくまずは初歩としてライブラリを公開してみたのが始まり。APIとしては単純で、カラーコード(hex)をStringで渡すと反転色を返してくれるというLibrary。Reactとかで最近DOMのStyleを直接弄ったりすることが多くて、ちょっとどうにかならないかなーと思って作った。(世の中にいっぱいある)
最近yarnのほうがよく使うので、自分のプロジェクトでyarnでinstallできればよきかなと思ったが、思った以上にかんたんだった。
今回はES6のclass構文を使って書いた。テストはFacebookが提供しているJest。 使い方はシンプル。
yarn add rcolor.js
import RColor from 'rcolor.js' const rcolor = new RColor('#ffffff') rcolor.toReverse() // #000000
packageの公開もすごく簡単だった。公式がおすすめ。
Firebase Day1
Firebase、今いろんなところで使われているGoogleが提供しているサービス。 昔むかしにUnityのデータ保存先としてRealtimeDatabaseを使ったことがあるが、もうわすれてしまった。 最近はReactとかにふれる機会が多いので、それをFirebaseのhostingに乗っけて見ようかと思った、ちょっとお試ししてみた。
なんかReactのプロジェクトをいい感じで作ってくれるコマンドがあるのでそれを打って見る。
$ npx create-react-app colors
今回は適当に色を組み合わせて保存するサービスを作ろうかなぁと思ったので、colorsにした。 まぁ特に意味はないんだけど。
$ cd colors $ firebase init
firebase-cliは別途インストールした。 initで対話形式でプロジェクトに必要なjsonを吐き出してくれる。なるほど。 ちなみに、プロジェクトをFirebase上で作って、リージョンを指定しないとプロジェクト選択でエラーが出るのでWebでポチポチっと。 なるほどなぁ。 そこはこの上ではできないんだ..って感じ。その後、デプロイしてみる。
$ npm run build $ firebase deploy
この2コマンドぐらいで行けるが、
Error: Cloud resource location is not set for this project but the operation you are attempting to perform in Cloud Firestore requires it
有効になっていないリソースがあればエラーになるっぽい。Web上でポチポチっと有効にしてみたらとりあえずOK。 今日はその後firebase Authenticationを利用してGoogleOauthのログインまで完了した。
FirebaseのAuthenticationを利用するところはこういう感じに書いた。
import React from 'react' import './App.css'; import firebase from "./firebase-config" class App extends React.Component { constructor(props) { super(props) this.state = { user: null } } render() { return( <div> <h1>Hello! Colors.</h1> { !this.isLogin && <button onClick={this.login}>Login</button> } { this.isLogin && <p>{this.state.user.email}</p> } </div> ) } componentDidMount() { firebase.auth().onAuthStateChanged((user) => { this.setState({ user }) }) } get isLogin() { return this.state.user && this.state.user.uid } login() { const provider = new firebase.auth.GoogleAuthProvider() firebase.auth().signInWithRedirect(provider) } } export default App;
これを ReactDOM.render
でレンダリングしてあげればいいんだなぁ。
firebaseのAPIを呼び出すときに毎回認証が必要なので、firebase-config.js
のように書き出しておく。
import firebase from 'firebase/app' import 'firebase/auth' const config = { apiKey: "APIKEY", authDomain: "Domain", databaseURL: "databaseURL", projectId: "projectID", storageBucket: "bucket", messagingSenderId: "senderid1234", appId: "1:hogehoge:web:1234" } firebase.initializeApp(config) export default firebase
ちなみにFirebaseのConfigのApiKeyはJSファイルに書き出されてしまう。これに対する答えは以下を見れば良さそうだ。
スマブラが発売されたけどホームランコンテストがなかったのでちょっと作ってみる
はじめに
みんなのウェディングのエンジニア@kazumalabです。 この記事はくふうカンパニーアドベントカレンダーのなんと10日目になります!
記事のお題をGraphQLにしてたのですが、タイミングがスマブラ発売と被ってしまいゲームのやりすぎで記事を書けそうありません。 (ほんとミス!)
ですのでお題をチェンジして書こうと思います。全くWeb関係からは離れます!←
スマッシュブラザーズSP
先ほども言いましたが、スマブラが発売されました。 みにいくだけ...と思ってふらっとヤマダ電機に行ったら PayPayで買えば20%Off、運良ければ全額返ってくるという「PayPayキャンペーン」をやっていました。
ええ、その誘惑に負けたわけですね。 ついつい買ってしまいました。
さらにアドカレ当日なのについついやってしまいました。それが以下の写真です。
今年の夏ぐらいにプロジェクターをかったのでその大画面でやるスマブラは格別でした。(そんなことは置いといて...)
今回のアドカレネタ
このスマブラ...ホームランコンテストがない!3DS・WiiUまではあったのに... え、じゃあ作ってみてもいいんじゃない?それをアドカレのネタにしちゃおう!というのが今回です。 まぁ結論からいうとホームランを撃ち抜くところまではいかなかったです...。けどダメージを受ければ受けるほどぶっ飛び率が上がっていくところはできたはずです!
そういえば以前に僕は格ゲーを作っていたのを覚えてますでしょうか...はるか昔の記憶です。
攻撃判定上手くいってるぞ。
— かずま@Pepro (@kazumalab) 2015年6月30日
ちょっとそれっぽくなったね! pic.twitter.com/Opnifd94Lt
これは3年前だったんですね。もっと最近だと思ってました。 時間もないのでどんどん作っていきましょー!
キャラクターを配置
右側がプレイヤ、左側がサンドバックくんです。 サンドバックはもう面倒なのでCylinderそのままです。笑
Playerは有料アセットを使っています。
プレイヤを動かす
今回は簡易的に作るのでCharactorControllerを使ってPlayer.csを実装します。
さらにPlayer
のタグをつけておきます。(のちに使う)
private CharacterController charactorController; void Start () { charactorController = this.GetComponent<CharacterController> (); } void Update () { direction = (Vector3.right * Input.GetAxis ("Horizontal") + Vector3.forward * Input.GetAxis ("Vertical")) * moveSpeed; Move (direction); } void Move (Vector3 v) { charactorController.Move (v); if (v.magnitude > 0.01f) { // 向いている方向に向かせる transform.rotation = Quaternion.LookRotation (v); } }
とりあえずこんな感じでセットします。 実際にはanimatorで歩くモーションを実装していますが今回は省いています。
あ、カメラはいい感じに(追従する感じなどなど)設定してくださいね!
攻撃する
さて、次はキモとなる攻撃部分を作っていきます。 スマブラではいくつか組み合わせ技がありますが、まずはパンチ一つに絞って実装します。(しかし複数使えるようにはしておく)
今回は与えるダメージ(吹っ飛ばし力)を計算するために必要なデータを準備します。
サンドバックくんのWeight
は50
に設定し、
プレイヤのパンチの威力は以下の図のようになりました。
DMG | BKB | KBG |
---|---|---|
5.0 | 30 | 100 |
BKBやKBGについては
上記サイトを参考にし、算出についてはスマッシュブラザーズ for WiiUの情報を参考にしました。
ちなみにダックハントのジャブと同じです。
以下のように先ほど動かすために作ったPlayer.csに追記します。
public Dictionary<string, Dictionary<string, float>> actions; public enum Actions { panch }; public Actions action; public bool enableHit = false; private void Start () { actions = new Dictionary<string, Dictionary<string, float>>(); Dictionary<string, float> actionInfo = new Dictionary<string, float>(); actionInfo.Add("DMG", 5.0f); actionInfo.Add("BKB", 30f); actionInfo.Add("KBG", 100f); actions.Add(Actions.punch.ToString(), actionInfo); } private void Update () { Attack(); } public void Attack () { if (Input.GetMouseButton (0)) { animator.SetInteger ("Attack", 4); action = Actions.punch; StartCoroutine (AttackTime ()); } else { animator.SetInteger ("Attack", 0); // Attackを0にしないと無限に攻撃が動く } } private IEnumerator AttackTime () { // すこし時間を開けないと攻撃があたり判定されない enableHit = true; yield return new WaitForSeconds (0.5f); enableHit = false; } public float getDamage () { return actions[action.ToString()]["DMG"]; } public float getKBG () { return actions[action.ToString()]["KBG"]; } public float getBKB() { return actions[action.ToString()]["BKB"]; }
actionsのkeyには攻撃名、valueには与えるダメージが入るようになっています。 Enumで定義したActions一覧からactionsを作ります。
今回はアニメーションをSetIntegerで設定しました。
enableHit
はtrue
の時だけ相手に攻撃が当たるようにしています。
action = Actions.punch;
ここでは何の攻撃をしたかを残すようにしています。 これでクリックをすると攻撃アニメーションになり、攻撃可能時間が0.5秒だけ有効になります。(ちょっと微妙仕様)
攻撃を与える、受ける
さてついにサンドバックくんが攻撃を受けるようにします。 まずは攻撃を受ける部分をEnemy.csを作り書いていきます。これはサンドバックくんにコンポーネントとして貼り付けます。
private Rigidbody rb; private float weight = 50f; private void Start() { rb = GetComponent<Rigidbody>(); } public void ReceiveDamage(Player target) { if (isHit) { isHit = false; hitpoint += target.getDamage(); if (rb) { // ここのVector.up本当は技の角度とユーザーのスティックから変更するべき rb.AddForce((target.transform.forward + Vector3.up) * KnockBack(target)); } } } private float KnockBack(Player target) { return ((hitpoint * (0.1f + target.getDamage() * 0.05f) * 200 / (weight + 100) * 1.4f + 18) * target.getKBG() * 0.01f + target.getBKB()); }
吹っ飛ばし力を計算する式が以下のサイトに載っていたので引用します。
KB = [{敵% * (0.1 + ダメージ * 0.05) * 200 / (敵重量 + 100) * 1.4 + 18} * KBG * 0.01 + BKB] * 補正値
これが先ほどEnemy.cs
に記述したKnockBack関数になります。
次に攻撃した時に当たった判定をする部分を実装します。
まずは攻撃する側(プレイヤ)の手の部分にColliderをつけます。この時isTrigger
にチェックを入れます。Rigidbodyはなくても良さそうです。
スクリプトを書き、手の部分にアタッチします。上の画像でいうとCollisionController.cs
になります。
public class CollisionController : MonoBehaviour { private Player player; private GameObject particle; // これは当たった時に発火するParticleなのでお好み private Enemy hitEnemy; // Use this for initialization void Start() { player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>(); } // Update is called once per frame void OnTriggerStay (Collider other) { if (other.tag == "Enemy") { hitEnemy = other.GetComponent<Enemy> (); if (player.enableHit && hitEnemy) { if (particle == null) { particle = (GameObject)Instantiate (player.hit_particles [0], this.transform.position, player.hit_particles [0].transform.rotation); other.GetComponent<Enemy>().isHit = true; other.GetComponent<Enemy>().ReceiveDamage(player); } } else { particle = null; hitEnemy = null; } } } }
あとは最後にUIをごにょっとすると...
ちょっとスマブラっぽくなりました!
まとめ
今回はノックバックのみの実装になってしまいましたが、継続して開発して「タメ技」とか「バットを使ったタメ技」などを入れてみたいですね。 なかなかスマブラを作るのは厳しいのであれは7000円でも安いと思う...
みんな、、、スマブラ休暇とろうな!(嘘です!!!!!!!!!!!!!!!!!笑)
今回書いたコードは本当はInterfaceを作ってベースとなるクラスを用意して、Enemy、Playerに継承する形にしていました。 ただ、多分そこまで解説できなさそうだったので、冗長ですが、別ものとしてブログ用に実装しました。ホームランコンテスト自分が攻撃を受けることないですし!