KazumaLab.

流行りとリラックマと嵐が大好きです。

スマブラが発売されたけどホームランコンテストがなかったのでちょっと作ってみる

はじめに

みんなのウェディングのエンジニア@kazumalabです。 この記事はくふうカンパニーアドベントカレンダーのなんと10日目になります!

記事のお題をGraphQLにしてたのですが、タイミングがスマブラ発売と被ってしまいゲームのやりすぎで記事を書けそうありません。 (ほんとミス!)

ですのでお題をチェンジして書こうと思います。全くWeb関係からは離れます!←

スマッシュブラザーズSP

先ほども言いましたが、スマブラが発売されました。 みにいくだけ...と思ってふらっとヤマダ電機に行ったら PayPayで買えば20%Off、運良ければ全額返ってくるという「PayPayキャンペーン」をやっていました。

ええ、その誘惑に負けたわけですね。 ついつい買ってしまいました。

さらにアドカレ当日なのについついやってしまいました。それが以下の写真です。

今年の夏ぐらいにプロジェクターをかったのでその大画面でやるスマブラは格別でした。(そんなことは置いといて...)

今回のアドカレネタ

このスマブラ...ホームランコンテストがない!3DSWiiUまではあったのに... え、じゃあ作ってみてもいいんじゃない?それをアドカレのネタにしちゃおう!というのが今回です。 まぁ結論からいうとホームランを撃ち抜くところまではいかなかったです...。けどダメージを受ければ受けるほどぶっ飛び率が上がっていくところはできたはずです!

そういえば以前に僕は格ゲーを作っていたのを覚えてますでしょうか...はるか昔の記憶です。

これは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で歩くモーションを実装していますが今回は省いています。

あ、カメラはいい感じに(追従する感じなどなど)設定してくださいね!

攻撃する

さて、次はキモとなる攻撃部分を作っていきます。 スマブラではいくつか組み合わせ技がありますが、まずはパンチ一つに絞って実装します。(しかし複数使えるようにはしておく)

今回は与えるダメージ(吹っ飛ばし力)を計算するために必要なデータを準備します。

サンドバックくんのWeight50に設定し、 プレイヤのパンチの威力は以下の図のようになりました。

DMG BKB KBG
5.0 30 100

BKBやKBGについては

www30.atwiki.jp

上記サイトを参考にし、算出についてはスマッシュブラザーズ for WiiUの情報を参考にしました。

Duck Hunt - Kurogane Hammer

ちなみにダックハントのジャブと同じです。

以下のように先ほど動かすために作った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); // Attack0にしないと無限に攻撃が動く
    }
}

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で設定しました。 enableHittrueの時だけ相手に攻撃が当たるようにしています。

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());
}

吹っ飛ばし力を計算する式が以下のサイトに載っていたので引用します。

www30.atwiki.jp

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に継承する形にしていました。 ただ、多分そこまで解説できなさそうだったので、冗長ですが、別ものとしてブログ用に実装しました。ホームランコンテスト自分が攻撃を受けることないですし!

明日は @tatsuo48さんのAWSの新サービスについて何か書いてくれるみたいです! お楽しみに!

Railsにおけるメモ化についてちょっと覗き見してみる

こんにちは! みんなのウェディングのエンジニア@kazumalabです。 この記事はくふうカンパニーアドベントカレンダーの3日目になります。

Rubyにおけるメモ化とは

||=を利用して、オブジェクトのキャッシュを行うことができます。 例えば、

def say
  "hello"
end

この場合sayメソッドを呼ぶ場合、振る舞いは同じなのに毎回違うStringクラスのインスタンスが生成されてしまいます。 確認してみます。

irb(main):010:0> say.__id__
=> 70107590117540
irb(main):011:0> say.__id__
=> 70107599432980

どうでしょうか。それではメモ化してみます。

def say
  @text ||= "hello"
end

このメモ化の流れの振る舞い以下のようになっています。

  • 左辺の@textを比較してnot nilだった場合その値を返す
  • @textがnilだった場合は"hello"を@textに代入して@textの値を返す

つまりは@textnilの状態でsayメソッドが呼ばれると@textに"hello"が代入されて、それ以降その@textに入っているオブジェクトを呼び続けるということになります。

irb(main):015:0> say.__id__
=> 70107599401820
irb(main):016:0> say.__id__
=> 70107599401820
irb(main):017:0> say.__id__
=> 70107599401820

何度呼び出しても同じオブジェクトのIDが返ってくることがわかります。

Railsでメモ化を使うケース

まずはメモ化をどういったところで使うのかを考えてみます。

  • Formオブジェクトのgetter
  • Decoratorパターンでメール送信をするときなど
  • APIを利用するとき

いろんなユースケースがあると思います。 さて、Railsでメモ化するとどれぐらいいい感じになるんでしょうか。

Attributesの呼ばれる回数

今回のケースとして、Formオブジェクトで利用する場合を例題にあげてみます。 BookモデルとAuthorモデルがあり、それを同時に生成するFormオブジェクトを以下のように記述してみます。

class BookForm
  include ActiveModel::Model

  attr_writer :name
  attr_accessor :title

  validates :title, :name, presence: true

  def save
    return unless valid?

    ActiveRecord::Base.transaction do
      book.save!
      author.save!
    end
  end

  def name
    @name ||= "NoName"
  end

  private

    def book
      @book ||= Book.new(title: title)
    end

    def author
      @author ||= book.authors.build(name: name)
    end
end

作者名を入力しない場合はNoNameを入れる仕様にしています。

流れ

  • /books/newにアクセス
  • /booksにデータをPOST
  • /books/:idにリダイレクト

/books/newにアクセス

さて、この作者の名前を返すnameメソッドにbinding.pryを仕組み、動きをみてみます。 まず止まったところは/books/newにアクセスしたときです。 /books/newではform_withを利用した以下のようなフォームを設置しています。

= form_with model: @book_form, url: books_path, method: :post, local: true do |f|
  - if f.object.errors.present?
    - f.object.errors.full_messages.each do |message|
      %p= message
  = f.label :title
  = f.text_field :title

  = f.label :name
  = f.text_field :name, value: @book.name
  = f.submit

text_fieldメソッドの中でAttributesが展開されているので、ここでまずは一回呼ばれました。この時点で@nameの中身はnilなのでメモ化されることになります。 exitでpryを抜けると/books/newが表示されました。

しかし、ここで一旦このオブジェクトの寿命は以上になります。

/booksにデータをPOSTする

さて、データを入力して、POSTをします。 Controllerでパラメータを受け取り、BookFormインスタンスを生成します。 その後保存するためのsaveメソッドを呼びますが、その中でvalidationが走ります。

def save
  return unless valid? # here

  ActiveRecord::Base.transaction do
    book.save!
    author.save!
  end
end

まずはそこで呼ばれました。まぁそうですね。nilだった場合はここでNoNameが代入されることになりますが、今回の検証で初めてわかったのですが、空文字だった場合はNoNameは代入されないみたいです。(仕様?)

次に呼ばれたのが、authorsaveするときに呼ばれました。

def author
  @author ||= book.authors.build(name: name)
end

空文字だった場合はメモ化が使えないのは以外でした。 このオブジェクトはバリデーションに失敗した時にはrender :newを呼ぶため、/books/newにアクセスしたときと同じ回数がプラスで呼ばれることになりそうです。今回の場合、validationに失敗した場合も、しなかった場合も3回呼ばれました。

最後に

(時間が間に合わなかったー) 今回の検証方法ではあまりメモ化の良さみたいなところがわかりにくかったと思います。 例えば

def book
  @book ||= Book.find_by(title: title)
end

みたいな感じでメモ化するとViewで何度も呼ばれてもDBへの問い合わせが一回で済みますよね。 そういったようにもう少し効果のわかりやすいのにして計測すればよかったですね。

明日のアドカレは!

@tastuoさんのAWS Lambda Layersを使ってみた!(Python)です!

【すいはんき工房】Youtuberをはじめました!

こんばんは!

かずまです。

 

最近更新できてなかった技術ブログですが、ここ最近ずっとYoutuberについて研究しておりました。笑

  つまりタイトルにも書いた通りYoutuberをとりあえず頑張ってみることにしました!🙌

 

チャンネル名は

【すいはんき工房】

www.youtube.com

珍しい開発系Youtuberとして活動します。エンジニアリングがわからない、プログラムがわからなくても楽しく見れるように作っています。もちろんわかる人にとってはより面白い動画になるよう頑張ります。

是非ともチャンネル登録してみてください!

最近の動画はこちら!

バンバンあげて行くのでよろしくお願いします!

InstagramTwitterもやってます!お見逃しなく! www.instagram.com

twitter.com

Webサービスを作る時にどれだけモチベを維持出来るかということ

かずまです。
今日は結構ふわっとしたお話です。
というより、最近「こういうサービスあったらいいな」と思って作ろうとするのですが、
モチベが続かず、ぼくのGithubにfirst commitと書いたcommitだけを残したリポジトリがあちらこちらに散らばっています。

サービスをいかに価値のあるものにするかというのは初歩のアイデアも大事ですが、
なにより作りきること、継続的に開発を進めていくことだと思っています。

これってゲームを作るのも同じ感覚でした。
なので、ゲームジャムとかで作ったものってシンプルだけど作りきるという意思で作っているから結構面白いものができるし、
そこに価値があると思うのです。

まぁゲームジャムはそこで終わっちゃうと残念ですが、
継続して続けられるならなおいいですよね。

最近作ろうと思ったものの例

  • 匿名性のSNS

これは完全匿名制で、情報をPOSTした際にそれに関連するタグを発行します。
そのタグをフォローしている人にその情報が届くシステムで、ユーザーに優位性を持たせて、自分が情報を投稿すればするほど
もらえるデータも増えるというサービスです。(Amyee)

続かなかった原因

  • セットアップ、設計に時間がかかってしまうこと。

WebサービスだとUser(ログイン)を必要とするサービスが多いです。最近だとOauthも標準的にログイン、会員登録として使われます。

個人で開発するときはRailsが多いのですが、
毎回ログイン周りをやろうとするとだるくてやめてしまう原因となります。

解決策

RailsではApplicationTemplateといった機能があります。
毎回これ入れるし、自動で作ってくれい!みたいな感じです。

それでさっとログインまでを作ってしまえば!と思ったりもします。

ginzarb.github.io

ここで@onkさんが話してたのを聞いて、いいなと思ったのですが、未だできてないのです...

  • 新しいことをやろうとする

これは今回のアプリケーションで使おうとしていた、PWAとか、技術的にはRailsAjaxだけでも全然できるんだけど、
ネイティブアプリっぽいデザインにしちゃったからPWAを使って通知とかしてみる?とか
Sinatoraで書くときにいつもは使わないのにWebpackerを使ってみたりとか
結構やらなくてもプロトタイプは作れるぞ!というのをいいたいのです(自分に)
ただ興味をもってしまうとたとえできなくても面白いのでやっちゃうのが現状です。
そして、導入できたら満足してしまう、もしくは上手く動かなくてサービス開発すら断念してしまう。

解決策

これは難しいと思います。と同時に引き合いだと思います。
今必ず必要な技術であれば、やる優先度を上げて、サービスがたとえできなくとも技術が学べただけでも大きいといったことであればどんどん挑戦していくべきだと思います。ただ、思いつきのサービスは実際作ってみると大したことないことがほぼほぼなので、簡単に作れる、一行でも書くコードが少なければ少ないほどいいですよね。

どっちがいいか、エンジニアとして幅を広げていくなら前者、サービスをひたすら作りたいのであれば後者みたいな感じかなぁ。
いい案があればコメントください。

最後に

最近こういう一人で作業しようとすると眠気くんが誘惑してきます。
布団の世界にこないか?こっちの世界は最高だぞと。

要はサービスを作る上で最も大事なのはある程度の技術は必要ですが、
モチベーションと健康は大切ですね...。

朝早く起きて、集中できる状態で、、、と思ったりして実行してみましたが、
9:00出社だ、あと2時間しかダメじゃん!みたいなことを考えるとかえって開発が進まなかったりしました。
自分にとって最高に集中できるのってなんだろうってお休みの間に考えたりするのもいいですね。

リモートでもローカルでもペアプロが行えるサービス"Pepro(β版)"をリリースしました

こんにちは。 かずまです。

saku氏とともにコツコツ作っていた、ペアプログラミングが行えるサービスをリリースしました。 それがPeproです。

www.pepro.me

まだまだスタートして間もないサービスですが、 ぜひとも使ってみてください。 * なお、若干つながりが悪いかもしれません。そういう場合はプログラムをCommand + Sで保存して、お互い再読込してみてください。

ペアプロとは

ペアプログラミングの略で、基本的に2人体制でプログラミングを行う開発手法です。 実際にやったことありますが、結構いいです。

  • めんどくさいところも絶対に進む
  • 詰まっているところが明確になる
  • その場でレビューもしてもらえる
  • お互いの開発のいい癖を盗める

デメリットとしては

  • 片方の作業が止まる
  • 1人じゃできない

が挙げられると思いますが、これら以外ではあまりデメリットはないイメージです。

Pepro

Peproの主な機能としてはペアプログラミングで大事な

  • 声に出すこと
  • 指差しすること
  • 一つのコードを一緒にみること

これらの機能を備えています。

声に出すこと

同じルームに入ると基本的に音声通話が始まります そこで話をしながら進められます。

指差しすること

今、ここのコードのことを言ってるんだよ!という確認や指示のときに使います。 基本的には画面をクリックしたところにポインターが当たるようになっています。

一つのコードを一緒に見る

Pepro を開くとエディタが一つだけあります。 片方がプログラムを書き始めると相手側の画面にもプログラムが同期されます。 (本来は同じ画面ですが...)

これでペアプログラミングを行うことができます。

おまけ

ペアプログラミングは喋って、頭を使って、プログラムを書きます。 その為、疲れてしまうのではないでしょうか?

Peproのルームを作る際にウェイティングページを準備しておりますので、その間にコーヒー等を一杯お持ちくださいませ。 準備ができ次第、ルームに入りましょう!

運営

Pepro開発ブログ

サービスURL

https://www.pepro.me

私にペアプロして!という感じの意味合い...!!