CS_INDUSTRIAL

AR/VRエンジニアの趣味ブログ

【GAS】WebアプリケーションでWebページを作る

GoogleAppScript(以下GAS)のWebアプリケーションの機能を使ってWebページを作ります。

GSuiteのアカウントではGSuiteメンバーのみが閲覧できるページしか作れないのでご注意ください。
通常のGoogleアカウントでは全体公開、自分のみ、Googleにログインしている人のみなど選べます。

GASの準備

過去記事を参考にGASの準備をお願いします。
madgenius.hateblo.jp

GASの記入

以下がページを表示するのに使うGASです。

  • doGetが必須のメソッドで、HTMLページを読み込むのに使います
    • ここではindex.htmlを読み込んで表示している
  • GetTextはHTMLページから情報を引き出すためのメソッドです。
    • 特定のKeyを送ることでそれに合った文字列を返します。
    • 本来、ここで通信したりスプレットシートの中身を見てページの表示内容を変更する
  • GetCSSではCSSの名前を送ることでそのCSSの中身を取得して返します。
function doGet() {
    var html = HtmlService.createTemplateFromFile("index");   
    return html.evaluate();
}

function GetText(key) {
    if (key == "title") {
        return "私が好きなプリパラアイドル";
    } else if (key == "rank1") {
        return "紫京院ひびき";
    } else if (key == "rank2") {
        return "北条そふぃ";
    } else if (key == "rank3") {
        return "東堂シオン";
    } else if (key == "rank4") {
        return "白玉みかん";
    } else if (key == "rank5") {
        return "み〜んなアイドル、み〜んな大好き";
    }
}

function GetCSS(filename) {
    return HtmlService.createHtmlOutputFromFile(filename).getContent();
}

CSSの作成

GetCSSで取得するCSSを作成します。
「GASのファイル > 新規作成」でHTMLファイルを作成します。
ファイル名は「css.html」です。
f:id:nanokanato:20170905161955p:plain:w300

<style>
body{
    font-family:Verdana,Arial;
    font-size:14px;
}
 
#header{
    margin-bottom:15px;
    border-bottom: 1px solid #ccc;
}
 
#contents{
}
 
#footer{
    font-size:12px;
    color:#ccc;
    text-align:center;
    border-top:1px solid #ccc;
    padding:10px 0 20px;
}
 
h2,h3{
    font-size:bold;
}
 
h2{
    font-size:16px;
    border-left:5px solid #ccc;
    padding:3px 0 3px 10px;
    margin-bottom:10px;
}
 
h3{
    border-bottom:1px solid #ccc;
    padding:3px 0;
    margin-bottom:10px;
}
 
p{
    margin-bottom:14px;
}
</style>

HTMLの作成

CSSの時と同じ手順でindex.htmlを作成します。

  • 「<head>」の中に「<?!= GetCSS('css'); ?>」とありますが、css.htmlの内容を<head>の中に追加しています。
    • 「<?= ?>」でメソッドの返り値をそのまま表示するとHTMLタグなどはエスケープされてWebページに文字列として表示されます。
      「<?!= ?>」を使うことによりエスケープをせずにタグとしてHTMLに埋め込むことが可能です。
  • 「<?= GetText("title"); ?>」はGetTextメソッドから"title"という文字に紐付けられた文字を返す。
<!DOCTYPE html>
<html>
    <head>
        <base target="_top">
        <?!= GetCSS('css'); ?>
    </head>
    <body>
        <section id="header">
           <h2><?= GetText("title"); ?></h2>
        </section>
        <section id="contents">
            <ul class="products">
                <li>
                    1位:<?= GetText("rank1"); ?>
                </li>
                <li>
                    2位:<?= GetText("rank2"); ?>
                </li>
                <li>
                    3位:<?= GetText("rank3"); ?>
                </li>
                <li>
                    4位:<?= GetText("rank4"); ?>
                </li>
                <li>
                    5位:<?= GetText("rank5"); ?>
                </li>
            </ul>
        </section>
        <section id="footer">
            <a href="https://twitter.com/ele_enji" target="_blank">園児ニアの技術メモ</a> / <a href="https://twitter.com/ele_enji" target="_blank">@ele_enji</a>
        </section>
    </body>
</html>

Webページを表示する

doGetメソッドを実行してエラーが出なければOKです。
たまにHTML内でのメソッド名などの入力ミスでエラーが出ます。

では、WebページとしてのURLアドレスを取得しましょう。

「公開 > ウェブ アプリケーションとして導入...」を選択。
f:id:nanokanato:20170905164041p:plain:w300

ウェブアプリケーションとして導入というポップアップが出ます。

  • アクセスユーザーを「自分だけ」から「全員(匿名ユーザーも含む)」にしました。
    • GSuiteの場合、「全員(hogehogeのメンバー)」など最大でもグループのユーザーしかアクセスできません。
  • もしアクセスしたユーザーのアカウントを使う必要がある場合は「次のユーザーとしてアプリケーションを実行」の実行ユーザーを「自分(hogehoge@hoge.com)」ではなく「ウェブアプリケーションにアクセスしているユーザー」にしましょう。

f:id:nanokanato:20170905164405p:plain:w300

更新を押すと実際のWebサイトのアドレスが取得できます。
f:id:nanokanato:20170905165018p:plain:w300

実際に今回作ったページがこちら
script.google.com

CSSも入れれるので結構いい感じのデザインにはできるのですが、URLの部分はGoogleAppScriptなんだなって感じなのでHPには向いてないです。
ツールサイトや、個人用として使うぶんには十分だと思います。
f:id:nanokanato:20170905165239p:plain:w300

【GAS】電車遅延情報をツイート

GoogleAppScript(以下GAS)にて電車遅延の情報をツイートする方法です。

過去記事でGASと連携してTwitter投稿を行う記事を書いてますのでどうぞ
madgenius.hateblo.jp

今回は上の定期ツイートの内容に電車遅延の情報を追加するのみになります。

電車遅延情報を取得してツイート

鉄道遅延情報のjsonを使って電車遅延情報を取得します。
ここがもし使えなくなった場合は各種運行会社のRSSを取得したり、公式Twitterを監視するのも使えます。(RSSなど公式の方が遅延理由もあるので便利ですが今回は簡単な方で...)

以下がソースです。10分毎のタイマーをセットしています。

//10分毎に鉄道遅延情報のjsonを取得して10分以内に更新された遅延情報をツイートする
function TrainDelayTweet() {
    var response = UrlFetchApp.fetch("https://rti-giken.jp/fhc/api/train_tetsudo/delay.json");
    var result = JSON.parse(response.getContentText());
    for(var i = 0; i < result.length; i++) {
        var company = result[i].company; //会社
        if (company == "東京メトロ" || company == "都営地下鉄") {
            var name = result[i].name; //路線
            var date = new Date(Number(result[i].lastupdate_gmt) * 1000); // 最終更新日時
            
            var Nowymdhms = new Date();
            if ((Nowymdhms.getTime()-date.getTime())/1000/60 < 10) {
                var ResultYear = date.getYear();
                var ResultMon = date.getMonth() + 1;
                var ResultDay = date.getDate();
                var ResultHour = date.getHours();
                var ResultMin = date.getMinutes();
                CS_Twitter.TwitterSend(name+"("+company+")が遅延しました。\n情報更新日時:"+ResultYear+"/"+ResultMon+"/"+ResultDay+" "+ResultHour+":"+ResultMin);
            }
        }
    }
}

10分毎に遅延情報を取得して更新時間が10分以内だったらツイートします。(しかし、普通に10分ごとに更新とかもあるので同じ路線が何度もツイートされることもある...)

私が都内に住んでいるので「JR九州」などを出さないために運行会社でフィルターをかけています。
ここでは東京メトロ都営地下鉄のみにしています。
JR東日本で山手線などを入れたいのですがJR東日本には東北も含まれますので、「JR東日本の山手線」の時はフィルターをスルーするようにしてやればOKです。

こちらのBotは私のTwitterにて動作確認が可能です。
以下、フィルターをかけていない時のツイートですが...

【GAS】Twitterの相互じゃない人を自動で定期的にリムーブ

はじめに

Twitterをやっていると前は相互フォローだったのに片思いになってしまった人などがいると思います。
片思いというのは、自分は相手をフォローしているが相手は自分をフォローしていない状態のことです。

しかし、誰と片思いになったかどうかを確認して1人1人リムーブするのは意外と手間がかかりますよね。

かといって一括ツールを使うと公式アカウントなど片思いと知っておきながらフォローしているアカウントまで解除されてしまいます。

GoogleAppScript(以下GAS)でやれば自分の思い通りにできるのでやってみました。

一応、手順とメソッド単位での説明がありますが、最後にソースだけをまとめています。

GASとTwitterAPIの連携

過去記事でGASとTwitterAPIを連携させるライブラリを作成しています。
こちらを参考に導入までどうぞ
madgenius.hateblo.jp

フォロー、フォロワーの取得

片思いかどうかを調べるためには自分のフォローとフォロワーのリストが必要です。

フォローの取得

CS_TwitterのTwitterFollowListを使用します。

//フォローしている人のリストを作成
function GetFollowList(screen_name,user_id) {
    var followCursor = -1;
    var followList = [];
    while (followCursor != 0) {
        var follow_list = CS_Twitter.TwitterFollowList(5000,screen_name,user_id,followCursor);
        followCursor = 0;
        if (follow_list != null) {
            if (follow_list.errors == null) {
                followCursor = follow_list.next_cursor;
                followList = followList.concat(follow_list.ids);
            } else {
                break;
            }
        }
    }
    return followList;
}
フォロワーの取得

CS_TwitterのTwitterFollowerListを使用します。

//フォロワーのリストを作成
function GetFollowerList(screen_name,user_id) {
    var followerCursor = -1;
    var followerList = [];
    while (followerCursor != 0) {
        var follower_list = CS_Twitter.TwitterFollowerList(5000,screen_name,user_id,followerCursor);
        followerCursor = 0;
        if (follower_list != null) {
            if (follower_list.errors == null) {
                followerCursor = follower_list.next_cursor;
                followerList = followerList.concat(follower_list.ids);
            } else {
                break;
            }
        }
    }
    return followerList;
}

一応これでフォロー全件とフォロワー全件が取得できるはずです。
1回の通信で5000人分なので超有名人でもないかぎりAPI規制がかかったりすることはないはずです。

片思いのユーザーのリストを作成

片思いを判定するのにフォローとフォロワーのリスト配列の差集合を求めます。(差集合でフォローにいてフォロワーにいない人だけのリストになる)

自力でもでき、処理スピード的には変わらないですが、Underscoreというライブラリを使うことにしました。
GASの「リソース > ライブラリ」を開き、Underscoreのプロジェクトキー「1PcEHcGVC1njZd8SfXtmgQk19djwVd2GrrW1gd7U5hNk033tzi6IUvIAV」を入力して追加を押してください。
f:id:nanokanato:20170906121147p:plain:w300

以下が片思いのリストを作るメソッドです。
引数

  • followList - フォローのリスト
  • followerList - フォロワーのリスト
//片思いのリストを作成
function GetUnrequitedList(followList, followerList) {
    var unrequitedList = [];
    //フォローしているのにフォロワーじゃないユーザーは解除する
    var _ = Underscore.load();
    unrequitedList = _.difference(followList,followerList);
    return unrequitedList;
}

user_idをscreen_nameに変更

片思いのリストを作ったのであとはリムーブするだけ....
残念、user_idでリムーブ可能と公式のTwitterAPIに書いてますがuser_idで探したけど見つからないみたいなエラーが出てほとんどリムーブできません。

なのでscreen_name(@hogehoge)に変換してあげます。
この変換の時はなぜか見つからないエラーが起きないのが救い...

CS_TwitterのTwitterUsersLookupで複数ユーザーを指定してその詳細からscreen_nameを取得します。

//user_idのリストをscreen_nameのリストに変更する
function ConvertScreenNameList(userIDList) {
    var screenNameList = [];
    while (userIDList.length > 0) {
        var idList = [];
        if (userIDList.length <= 100) {
            idList = userIDList.slice(0);
            userIDList = [];
        } else {
            idList = userIDList.slice(0,100);
            userIDList = userIDList.slice(100);
        }
        var ids = "";
        for (var i = 0; i < idList.length; i++) {
            var id = idList[i];
            if (ids != "") {
                ids += ",";
            }
            ids += id;
        }
        if (ids != "") {
            var lockups = CS_Twitter.TwitterUsersLookup(null,ids);
            if (lockups == null) {
                break;
            } else {
                if (lockups.errors == null) {
                    for (var i = 0; i < lockups.length; i++) {
                        var lockup = lockups[i];
                        if (lockup != null) {
                            if (lockup.screen_name != null) {
                                screenNameList[screenNameList.length] = lockup.screen_name;
                            }
                        }
                    }
                } else {
                    break;
                }
            }
        }
    }
    return screenNameList;
}

これで片思いのユーザーのscreen_nameのリストが取得できました。

「var lockup = lockups[i];」の部分でユーザーの詳細があるので特定のユーザーは片思いでも見逃してスルーするなどの処理を入れることができます。(リムーブ時にscreen_nameで判別してでも良い)

上のメソッドを利用してリムーブまで行う

こんどこそ片思いのリストが用意できたのであとはリムーブするだけです。

リムーブはを使用します。


以下が片思いを取得してリムーブするメソッドです。
このメソッドをタイマーなどで呼べば定期的にリムーブすることができます。

//片思いを解除する
function UnFollow() {
    var user_id = "0000000000";
    //フォローしているユーザーのリストを作成
    var followList = GetFollowList(null,user_id);
    if (followList != null && followList.length > 0) {
        //フォロワーのリストを作成
        var followerList = GetFollowerList(null,user_id);
        if (followerList != null && followerList.length > 0) {
            //片思いのリストを作成
            var unrequitedList = GetUnrequitedList(followList, followerList);
            if (unrequitedList != null && unrequitedList.length > 0) {
                //user_idのリストをscreen_nameに変更
                var screenNameList = ConvertScreenNameList(unrequitedList);
                if (screenNameList != null && screenNameList.length > 0) {
                    //フォローを解除する
                    for (var i = 0; i < screenNameList.length; i++) {
                        var screen_name = screenNameList[i];
                        if (screen_name != null) {
                            var unfollow = CS_Twitter.TwitterUnfollow(screen_name, null);
                            if (unfollow == null || (unfollow != null && unfollow.errors != null)) {
                                
                            }
                        }
                    }
                }
            }
        }
    }
}

全ソース

TwitterAPIの連携や通信周りをまとめたスクリプトファイル

AutoUnFollowを毎分のタイマーに設定することで毎日0時0分に片思いを解除してくれる。
Unfollow.gs

CS_Twitter.TWITTER_CONSUMER_KEY = 'CONSUMER_KEY';
CS_Twitter.TWITTER_CONSUMER_SECRET = 'CONSUMER_SECRET';
CS_Twitter.OAUTH_USER_KEY = 'Unfollow';

//片思いフォローしているユーザーへのフォローを解除する
//毎日0時0分
function AutoUnFollow() {
    var Nowymdhms = new Date();
    var NowHour = Nowymdhms.getHours();
    var NowMin = Nowymdhms.getMinutes();
    if (NowHour == 0 && NowMin == 0) {
        UnFollow();
    }
}

//片思いを解除する
function UnFollow() {
    var user_id = "your user id";
    //フォローしているユーザーのリストを作成
    var followList = GetFollowList(null,user_id);
    if (followList != null && followList.length > 0) {
        //フォロワーのリストを作成
        var followerList = GetFollowerList(null,user_id);
        if (followerList != null && followerList.length > 0) {
            //片思いのリストを作成
            var unrequitedList = GetUnrequitedList(followList, followerList);
            if (unrequitedList != null && unrequitedList.length > 0) {
                //user_idのリストをscreen_nameに変更
                var screenNameList = ConvertScreenNameList(unrequitedList);
                if (screenNameList != null && screenNameList.length > 0) {
                    //フォローを解除する
                    for (var i = 0; i < screenNameList.length; i++) {
                        var screen_name = screenNameList[i];
                        if (screen_name != null) {
                            var unfollow = CS_Twitter.TwitterUnfollow(screen_name, null);
                            if (unfollow == null || (unfollow != null && unfollow.errors != null)) {
                                
                            }
                        }
                    }
                }
            }
        }
    }
}

//user_idのリストをscreen_nameのリストに変更する
function ConvertScreenNameList(userIDList) {
    var screenNameList = [];
    while (userIDList.length > 0) {
        var idList = [];
        if (userIDList.length <= 100) {
            idList = userIDList.slice(0);
            userIDList = [];
        } else {
            idList = userIDList.slice(0,100);
            userIDList = userIDList.slice(100);
        }
        var ids = "";
        for (var i = 0; i < idList.length; i++) {
            var id = idList[i];
            if (ids != "") {
                ids += ",";
            }
            ids += id;
        }
        if (ids != "") {
            var lockups = CS_Twitter.TwitterUsersLookup(null,ids);
            if (lockups == null) {
                break;
            } else {
                if (lockups.errors == null) {
                    for (var i = 0; i < lockups.length; i++) {
                        var lockup = lockups[i];
                        if (lockup != null) {
                            if (lockup.screen_name != null) {
                                screenNameList[screenNameList.length] = lockup.screen_name;
                            }
                        }
                    }
                } else {
                    break;
                }
            }
        }
    }
    return screenNameList;
}

//片思いのリストを作成
function GetUnrequitedList(followList, followerList) {
    var unrequitedList = [];
    //フォローしているのにフォロワーじゃないユーザーは解除する
    var _ = Underscore.load();
    unrequitedList = _.difference(followList,followerList);
    return unrequitedList;
}

//フォローしている人のリストを作成
function GetFollowList(screen_name,user_id) {
    var followCursor = -1;
    var followList = [];
    while (followCursor != 0) {
        var follow_list = CS_Twitter.TwitterFollowList(5000,screen_name,user_id,followCursor);
        followCursor = 0;
        if (follow_list != null) {
            if (follow_list.errors == null) {
                followCursor = follow_list.next_cursor;
                followList = followList.concat(follow_list.ids);
            } else {
                break;
            }
        }
    }
    return followList;
}

//フォロワーのリストを作成
function GetFollowerList(screen_name,user_id) {
    var followerCursor = -1;
    var followerList = [];
    while (followerCursor != 0) {
        var follower_list = CS_Twitter.TwitterFollowerList(5000,screen_name,user_id,followerCursor);
        followerCursor = 0;
        if (follower_list != null) {
            if (follower_list.errors == null) {
                followerCursor = follower_list.next_cursor;
                followerList = followerList.concat(follower_list.ids);
            } else {
                break;
            }
        }
    }
    return followerList;
}

余談

1回の実行で片思いが多すぎる人などは途中でTwitterAPIの規制や、GASのURLFetchの規制で処理が途中で終わる場合があります。

過去記事の応用などもあり、省略部分があるかと思いますので不明点やエラーが出た場合はコメントにお願いします。

【GAS】時報をツイートする

はじめに

GoogleAppScript(以下GAS)を使って1時間ごとに時刻が変わったことをTwitterに報告するBotを作ります。

GASで定期ツイートをするまでは以下をどうぞ。
madgenius.hateblo.jp

今回は上の定期ツイートの記事に正確性と実行時間の取得を追加するだけになります。

ソース

特に変更が必要な場所はありません、このまま使用できます。
タイマーは毎分で設定してください。

//1時間ごとに時刻をツイートする
function HourTweet() {
    var Nowymdhms = new Date();
    var NowYear = Nowymdhms.getYear();
    var NowMon = Nowymdhms.getMonth() + 1;
    var NowDay = Nowymdhms.getDate();
    var NowHour = Nowymdhms.getHours();
    var NowMin = Nowymdhms.getMinutes();
    if (NowMin == 0) {
        CS_Twitter.TwitterSend(NowHour+"時になりました。\n("+ZeroNum(NowYear,4)+"/"+ZeroNum(NowMon,2)+"/"+ZeroNum(NowDay,2)+")");
    }
}

function ZeroNum(num,zero) {
    var str = ""+num;
    for (var i = 0; i < zero; i++) {
        str = "0"+str;     
    }
    return str.slice(-zero);
}

ただ、タイマーで毎分チェックして0分の時に時報を流すだけです。
ぴったりの時間にお知らせできました。
f:id:nanokanato:20170829121443p:plain:w300

余談

Twitterに投稿している部分を他のSNSに変更することもできます。
ChatWorkへの投稿については以下をどうぞ。
madgenius.hateblo.jp

【GAS】Twitterと連携して定期ツイート

GoogleAppScript(以下GAS)からTwitterと連携して定期ツイートするまでを書きます。

GASの用意

GASの始め方がわからない人は過去の記事を参考にしてね。
madgenius.hateblo.jp

とりあえず、用意したらTwitterAPIでPROJECT_KEYを取得するのに必要なので開いたまま次へ

TwitterAPIの作成

TwitterAPIを使用するにはKeyとSecretが必要です。
準備までの手順は以下を参考にどうぞ。
madgenius.hateblo.jp

CallBack URLの部分はGASなので「https://script.google.com/macros/d/{PROJECT_KEY}/usercallback」になるように気をつけてください。
その辺りも上の記事に書いています。

GASライブラリを用意

GASでTwitter連携するためにはKeyとSecretを利用してOAuth認証が必要です。
OAuth認証と言われても結構面倒なのでそれを簡単にするライブラリを用意しました。
導入方法や使い方も書いてありますのでどうぞ。

madgenius.hateblo.jp

GASでツイート

以下がソースです。
ライブラリが入ってないと動きませんが、上で追加していれば動きます。

CS_Twitter.TWITTER_CONSUMER_KEY = 'API_KEY';
CS_Twitter.TWITTER_CONSUMER_SECRET = 'API_SECRET';
CS_Twitter.OAUTH_USER_KEY = 'AutoTweetBot';

function AutoTweet() {
        CS_Twitter.TwitterSend("定期ツイートです。");
    }
}
  • API_KEY、API_SECRETはTwitterから取得したものに置き換えてください。

AutoTweet()をタイマーで呼んでやることでタイマーのタイミングで「定期ツイートです。」とツイートします。

TwitterAPIを利用するまでの登録など

Twitterと連携するアプリやウェブサイトを作るためにはTwitter DevelopersでAppとして登録する必要があります。
登録といってもTwitterアカウントがあれば無料でできるので安心してください。

TwitterDevelopersへ移動

以下より、TwitterAPIが作成できます。
Welcome — Twitter Developers

2017年8月現在ではこんなレイアウトです。
ちょっと前と変わりましたが基本的な配置は一緒です。
f:id:nanokanato:20170828134704p:plain:w300

右上のMy appsをクリック

My appsを開く

Twitterにログインしていない場合はこんな感じ。
ログインしましょう。
f:id:nanokanato:20170828135110p:plain:w300

まだ、Appを何も登録していない場合。
f:id:nanokanato:20170828134843p:plain:w300

すでにAppを登録している場合。
App名や説明は別に見られてもいいものですが、何をしているかバレて恥ずかしいので隠しました。
f:id:nanokanato:20170828135353p:plain:w300

画面の「Create New App」からAppを新規作成できます。

Appの新規作成

新規作成のページではApp名、利用目的の説明、ウェブサイト、CallBackURLの記入があります。
f:id:nanokanato:20170828135809p:plain

  • Name(必須)
    • App名
    • Twitter連携時に表示されます。
  • Description(必須)
    • 利用目的の説明
    • Twitter連携時に表示されます。
  • Website(必須)
    • 開発者のサイト
    • ブログがあればそれでもいいですし、簡単なのはTwitterのアカウントのページです。
    • Twitter連携時に表示されます。
  • CallBack URL
    • アプリ連携の場合は基本未記入です。
    • Botサービスなど外部サイトの場合、利用方法にCallBackにこのアドレスを記入と指示があると思います。
    • GoogleAppScriptの場合は「https://script.google.com/macros/d/{PROJECT KEY}/usercallback」を記入します。
      • PROJECT KEYはGoogleAppScriptで「ファイル > プロジェクトのプロパティ」で表示されたウィンドウのスクリプトIDと同じです。
        f:id:nanokanato:20170828143429p:plain:w300
      • また、PROJECT KEYは以下の画像の赤い部分です。
        f:id:nanokanato:20170828140953p:plain:w300
        URLは「script.google.com/a/hogehoge/d/{PROJECT KEY}/edit?usp=drive_web」となっています。
        最後の「/edit?usp=drive_web」はいりません。

入力したらDeveloper Agreementのところの利用規約に同意のチェックを入れて「Create your Twitter application」をクリック。

Appを登録するとその詳細ページに遷移すると思います。
遷移しない場合はMy Appsから探してください。
f:id:nanokanato:20170828141652p:plain:w300

Permissionsの確認

「Permissions」を選択することでそのAppが使えるTwitterAPIの権限が閲覧、編集できます。
f:id:nanokanato:20170828141920p:plain:w300

通常はRead and Writeですが、Twitterのデータを閲覧するだけでいいならRead only、DMの送信も行うならRead, Write and Access direct messagesに変更してください。
連携時にこの権限を許可しますか?という感じで表示されるので自分以外も使うのであれば適切なものを選びましょう。

KeyとSecretの取得

「Keys and Access Tokens」のタブを選択してください。
f:id:nanokanato:20170828142611p:plain:w300

ここのKeyとSecretの部分を使ってTwitterAPIを利用します。

【GAS】自動送信じゃないGmailをSNSに通知

私はそんなにメールを使わないのですが、
メールを使う営業の方からしてみればセミナーに参加した時などの自動送信メールに埋もれた個人宛の返信待ちのメールを見逃しがちなのでは?という発送で作りました。

GoogleAppScript(以下GAS)はGmailを持っていれば使えるのでエンジニア以外でも使えるのがいいですね。

機能としては

  • 個人宛の場合に必ず入っている単語、ここでは苗字を含む未読のメールを20件まで検索
  • 検索結果から特定のアドレスや単語を含むメールを排除し、10分以内に受け取ったものだけにする。
    • 残ったメール内容は何回も通知しないように既読にします。
  • 残ったメール内容をSNSに通知する。

GASとSNSの準備

以下、今回使うGASとChatWorkについての記事です。
GASを使用する準備は以下を参考にどうぞ。
madgenius.hateblo.jp

下のソースの最後にも同じ内容が入っていますが、GASでChatWorkに投稿する方法は以下を参考にしてください。
madgenius.hateblo.jp

その他APIが公開されているTwitterやSlackなども投稿部分の改変次第で利用可能です。

ソース

以下、実際にソース内容です。

function MinuteCheckGMail() {
    /*---------------------------------------*
     * 未読で苗字を含んでいるメールを新規100件取得
     *---------------------------------------*/
    var strTerms = 'is:unread "あなたの苗字"'; //未読で苗字を含むもの
    var myThreads = GmailApp.search(strTerms, 0, 20); //条件にマッチしたスレッドを100件取得
    var myMsgs = GmailApp.getMessagesForThreads(myThreads); //スレッドからメールを取得する →二次元配列で格納
    var valMsgs = [];
    var dateNow = new Date();
    
    /*---------------------------------------*
     * 10分以上前のものや自動配信のものを除外
     * 除外されないものはChatWorkに送るので既読にする
     *---------------------------------------*/
    var j = 0;
    for(var i = 0;i < myMsgs.length;i++){
        var myThread = myThreads[i];
        var myMsg = myMsgs[i];
        if (myMsg != null) {
            var date = myMsg[0].getDate();
            var from = myMsg[0].getFrom();
            var subject = myMsg[0].getSubject();
            var body = myMsg[0].getPlainBody();
            if (!from.match(/block_user@gmail.com/)) { //特定のアドレスからの通知を拒否
                //自動送信のニュースを拒否(Googleカレンダー,ChatWorkなど)
                if (!body.match(/配信停止/) && !body.match(/受信設定/) && !body.match(/通知設定/)) {
                    //受信日時が今じゃないなら拒否(10分前のものまで受けとる)
                    if (dateNow.getFullYear() == date.getFullYear()) {
                        if (dateNow.getMonth() == date.getMonth()) {
                            if (dateNow.getDate() == date.getDate()) {
                                if (dateNow.getHours() == date.getHours()) {
                                    if (dateNow.getMinutes()-10 <= date.getMinutes()) {
                                        myThread.markRead(); //ChatWorkに送信したものは既読にする
                                        valMsgs[j] = [];
                                        valMsgs[j][0] = date;
                                        valMsgs[j][1] = from;
                                        valMsgs[j][2] = subject;
                                        valMsgs[j][3] = body;
                                        j++;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    /*---------------------------------------*
     * 自分宛ての新着メールをチャットワークに送信する
     *---------------------------------------*/
    if (valMsgs.length > 0) {
        for (var i = 0; i < valMsgs.length; i++) {
            var valMsg = valMsgs[i];
            if (valMsg != null) {
                var strBody = "";
                strBody += "[info][title]"+valMsg[1]+"[/title]";
                strBody += "受信日時:"+valMsg[0].getFullYear()+"/"+valMsg[0].getMonth()+"/"+valMsg[0].getDate()+" "+valMsg[0].getHours()+":"+valMsg[0].getMinutes()+":"+valMsg[0].getSeconds()+"\n";
                strBody += "タイトル:"+valMsg[2]+"\n";
                strBody += "本文  :"+"\n"+valMsg[3];
                strBody += "[/info]";
                
                //ChatWorkに予定一覧を投稿する
                var client = ChatWorkClient.factory({token:"????????"});
                client.sendMessage({
                    room_id:00000000,
                    body:strBody
                });
            }
        }
    }
}
  • 実行時に認証するGoogleのアカウントに紐づくGmailを確認します。
    • タイマー設定は毎分にしてください。
  • 「'is:unread "あなたの苗字"';」の部分の「あなたの苗字」を苗字に変えてください。
  • 「if (!from.match(/block_user@gmail.com/)) {」の部分が特定のアドレスからの受信は通知しないように設定している部分です。
    • 複数設定する場合は「if (!from.match(/block_user@gmail.com/) && !from.match(/block_user_2@gmail.com/)) {」のようにしてください。
  • 「if (!body.match(/配信停止/)) {」の部分で「配信停止」を含むメールを除外しています。

動作

hogehoge@gmailから自分が認証したアカウントに「あなたの苗字」の部分を「苗字」に変えて送信した場合です。
f:id:nanokanato:20170828113954p:plain:w300

たまに2分ほど遅れて通知がきますが、未読であればちゃんと通知してくれます。
一応、同じhogehoge@gmailから「このメールは自動送信です。配信停止はこちら」みたいなメールを送りましたが、それは除外しているのでSNSに通知がきませんでした。

余談

GASでTwitterにツイートする記事です。
ツイート部分のアドレス(/update.json)を自分宛にDMを送信などにすれば使えます。
仕事のメールはChatWorkに、個人のメールはTwitterのDMになど変更可能です。
madgenius.hateblo.jp

【GAS】Googleカレンダーの予定をSNSへ通知

GoogleAppScript(以下GASと略称)でGoogleカレンダーの予定を自分のSNSにお知らせしてくれるツールを作りました。

普段、カレンダーを見る癖がない私ですが、SNSはよく見るのでSNSで自分宛に予定がくれば楽なので...

機能としては

  • 1週間後の予定まで確認(改変可能)
  • 予定の日時、または通知を設定した時刻(予定の10分前など)になったらSNSで自分宛に通知

SNSの部分は今回はChatWorkになっていますが、
仕事の予定はChatWork、個人はTwitterのDMなどの改変可能です。

GASを使用する準備は以下を参考にどうぞ。
madgenius.hateblo.jp

下のソースの最後にも同じ内容が入っていますが、GASでChatWorkに投稿する方法は以下を参考にしてください。
madgenius.hateblo.jp

以下、Googleカレンダーの予定をChatWorkに投稿するソース

/*------------------------------------*
 * 毎分カレンダーから1週間先までの予定を見る。
 * ↓
 * 予定が現在、または通知を登録していた時間だった場合お知らせする
 *------------------------------------*/
function MinuteCheckCalender() {
    //カレンダーからイベントの取得
    var myCals = CalendarApp.getCalendarById('hogehoge@gmail.com'); //特定のIDのカレンダーを取得
    if (myCals != null) { //権限がないなどの時はnullになるので処理をスルーする
        //カレンダーから現在〜1週間後までのイベントを取得
        var startDate = new Date();
        startDate = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate(), startDate.getHours(), startDate.getMinutes(), 0);
        var endDate = new Date();
        endDate.setDate(endDate.getDate()+7);
        var myEvents = myCals.getEvents(startDate, endDate);
        
        if (myEvents.length > 0) { //カレンダーに予定がない時はスルーする
            //お知らせが必要な予定一覧を作成する
            var strBody = "";
            for(var i = 0; i < myEvents.length; i++){
                var alertTime = -1;
                var strStart = myEvents[i].getStartTime(); //イベントの開始時刻
                //予定の日付と今日が同じかチェックする
                if (startDate.getFullYear() == strStart.getFullYear()) {
                    if (startDate.getMonth() == strStart.getMonth()) {
                        if (startDate.getDate() == strStart.getDate()) {
                            //予定の日時と今が同じ時分かチェックする
                            if (startDate.getHours() == strStart.getHours()) {
                                if (startDate.getMinutes() == strStart.getMinutes()) {
                                    //何分前の通知かを保持
                                    alertTime = 0;
                                }
                            }
                        }
                    }
                }
                        
                var intReminders = myEvents[i].getPopupReminders(); //何分前にお知らせするか
                if (intReminders != null) {
                    if (intReminders.length > 0) {
                        //予定の日時じゃなくても通知が登録されていれば通知時間にお知らせする
                        if (alertTime == -1) {
                            for (var j = 0; j < intReminders.length; j++) {
                                //通知として登録していた日時を取得
                                var minute = intReminders[j];
                                var alertDate = new Date(strStart.getTime());
                                alertDate.setMinutes(alertDate.getMinutes()-minute);
                                
                                //通知の日時と今日が同じかチェックする
                                if (startDate.getFullYear() == alertDate.getFullYear()) {
                                    if (startDate.getMonth() == alertDate.getMonth()) {
                                        if (startDate.getDate() == alertDate.getDate()) {
                                            //通知の日時と今が同じ時分かチェックする
                                            if (startDate.getHours() == alertDate.getHours()) {
                                                if (startDate.getMinutes() == alertDate.getMinutes()) {
                                                    //何分前の通知かを保持
                                                    alertTime = minute;
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                
                if (alertTime >= 0) {
                    var strTitle = myEvents[i].getTitle(); //イベントのタイトル
                    var strEvent = "";
                    if (strBody != "") { //他の予定の後に追加する場合は空白行を追加する
                        strEvent += "\n\n";
                    }
                    if (alertTime == 0) {
                        strEvent += "[info][title]予定の時刻です[/title]";
                    } else {
                        if (alertTime < 60) {
                            strEvent += "[info][title]予定の"+alertTime+"分前です[/title]";
                        } else if (alertTime < 24*60) {
                            strEvent += "[info][title]予定の"+(alertTime/60)+"時間前です[/title]";
                        } else {
                            strEvent += "[info][title]予定の"+(alertTime/60/24)+"日前です[/title]";
                        }
                    }
                    strEvent += "件名:"+strTitle+"\n";
                    if (myEvents[i].isAllDayEvent()) {
                        strEvent += "時間:終日\n";
                    } else {
                        var strEnd = myEvents[i].getEndTime(); //イベントの終了時刻
                        strEvent += "時間:"+Utilities.formatDate(strStart,'JST','HH:mm')+"~"+Utilities.formatDate(strEnd,'JST','HH:mm')+"\n";
                    }
                    var strLocation = myEvents[i].getLocation(); //場所
                    strEvent += "場所:"+strLocation+"\n";
                    var strDescription = myEvents[i].getDescription(); //説明
                    strEvent += "説明:"+strDescription;
                    strEvent += "[/info]";
                    if (strEvent != "") {
                        strBody += strEvent;
                    }
                }
            }
            
            if (strBody != "") {
                //ChatWorkに予定一覧を投稿する
                var client = ChatWorkClient.factory({token:"???????????????"});
                client.sendMessage({
                    room_id:00000000,
                    body:strBody}
                );
            }
        }
    }
}
  • 最初のほうの「'hogehoge@gmail.com'」を自分のカレンダーを使っているGoogleのアドレスにしてください。
    • GSuiteなど企業用のアカウントで独自のドメインのものでも可能です。
  • 最後のChatWorkに投稿部分のAPIトークンとルームIDを変更してください。
    • わからない方はソースの上の「GASでChatWorkに投稿する方法」の記事の中で説明していますので参考に。

動作

実際に動かすとこんな感じ。
f:id:nanokanato:20170828110549p:plain:w300
自分から自分に送るのではなく、Bot用のアカウントから自分に送ると通知がくるのでオススメです。
さらにSNSスマホ用アプリを入れておくとPush通知でも確認できます。

現在、終日の場合は「時間:終日」となりますが予定日の0:00に通知がくるのでそこらへんは改変して使ってください。

余談

GASでTwitterにツイートする記事です。
ツイート部分のアドレス(/update.json)を自分宛にDMを送信などにすれば使えます。
仕事の予定はChatWorkに、個人の予定はTwitterのDMになどと使い分けが可能です。
madgenius.hateblo.jp

【GAS】ChatWorkに投稿

GoogleAppScript(以下GASと省略)でChatWorkに投稿する方法です。

ChatWorkAPIトークンの発行

まずはChatWorkAPIを使用するためにChatWorkからAPIトークンを発行する必要があります。

以下がChatWork公式のAPIドキュメントです。
developer.chatwork.com

ドキュメントの通り、動作設定を開き
f:id:nanokanato:20170828102714p:plain:w300

出てきたウィンドウのAPI発行のタブを表示して、パスワードを入力すればAPIトークンが表示されます。
f:id:nanokanato:20170828103023p:plain:w300

初回はAPIトークンが発行されていないので発行待ちがあるのと、ChatWorkに個人ではなく企業で契約していると管理者しかAPI発行のタブが表示されませんのでご注意ください。

GASに書き込み

APIトークンの取得に成功したら、GASで投稿してみましょう。
GASの準備などはこちらにまとめていますので参考にどうぞ。
madgenius.hateblo.jp

ChatWorkClientというライブラリを使用しますので「リソース > ライブラリ」を開き、プロジェクトキーに「1nf253qsOnZ-RcdcFu1Y2v4pGwTuuDxN5EbuvKEZprBWg764tjwA5fLav」を入力してライブラリを追加してください。
f:id:nanokanato:20170829123103p:plain:w300

以下のメソッドに引数を渡してあげるとその引数の文字列を投稿してくれます。

function ChatWorkSend(strBody) {
    //ChatWorkに予定一覧を投稿する
    var client = ChatWorkClient.factory({token:"????????"});
    client.sendMessage({
        room_id:00000000,
        body:strBody
    });
}
  • 「token:"????????"」の????????の部分に先ほど取得したAPIトークンを書き換え。
  • 「room_id:00000000」の00000000の部分にChatWorkのルームIDを設定してください。
    • ルームIDはAPIトークンを発行したアカウントが投稿できるルームを開きブラウザのURLの「kcw.kddi.ne.jp/#!rid00000000」のrid以下の数字になります。
    • また、アプリ版を使っている場合はURLが見れませんが、メッセージのURLをコピーするリンクボタンを押すとridが取得できます。

利用方法

簡単な例としては...
「ChatWorkSend("おはよう")」などをトリガーで日タイマー,午前7時~8時で登録すれば毎朝7時~8時に「おはよう」とChatWorkに投稿してくれます。

これだけではただのBotですが、GASはGoogleのサービスはもちろんTwitterなどAPIが公開されているサービスと連携できるのでいろいろな使い道があります。
面白い使い道があればぜひ私にも教えてください。

【Unity】Xcodeから実機ビルド時に「App Installation Failed」

はじめに

Unityから書き出したプロジェクトをXcodeで開きiPhoneへインストールしようとした時、以下のエラーが起きた。

`[App Installation Failed] Could not write to the device.`

`[App Installation Failed] This application does not support this device’s CPU type.`

XcodeでRun時にメッセージウィンドウとして表示されます。
頻度としては4回実行して上のエラーが3回に対して、下のエラーは1回くらいです。

動作環境

iPhone7(iOS11 beta)
XCode 9.0 Beta 3
MacBook PromacOS Sierra10.12.5)
UnityPro(5.6.2f1)

そうなった経緯

  1. 朝、問題になっているプロジェクトにて実機インストールができる状態であったことを確認。
  2. デバッグのためUnityのBuild SettingsにてDevelopment BuildとAutoconnect Profilerにチェックを入れる。
  3. その後、以下の記事を参考にIl2cppだったプロジェクトをMono2xに変更する。

www.f-sp.com

すると、エラーが出てインストールができなくなりました。

解決のためにやったこと

Mono2xからIl2cppに戻す

効果なし...

Unity Development Buildを解除

UnityのBuild SettingsにてDevelopment BuildとAutoconnect Profilerにチェックが入っていたので外したが効果なし。

以下は普通にエラー文で検索してみていろいろやってみました

正しいArchitecturesの設定かどうか

teratail.com
Targetの「Build Settings > Architectures > Valid Architectures」にiPhone7のarm64が追加されているか確認したが追加されていた...

容量

gootara.org
iPhoneの容量が足りず入れられない時にエラーが出るとあり、
iPhoneの容量が足りているか確認したが121.6GBも残っていた...

stackoverflow.com
Macの容量が足りないとインストールできずにエラーが出る的なことが英語で書いてあったのでMacの容量が足りているか確認したが67.09GB残っていた...

インストールしてある同じアプリを消す

qiita.com
インストールしているアプリを削除し、再度XcodeでRunしてインストールしようとしたが同じエラーが出た...

CocoaPods

neighborhood.bluz.io
CocoaPodsを使用している場合は.xcodeprojファイルではなく、.xcworkspaceファイルから起動しなくてはいけない。

forums.developer.apple.com
CocoaPodsを更新することで解決すると書かれている。

しかし、このプロジェクトはUnityでCocoaPodsは未使用です...

記事がなくなってきたので汎用的な祈りの儀式を始めます

再起動

iPhone,Macの再起動 & Xcodeのプロジェクトを消してUnityから再度ビルドしたが同じエラーは健在...

Gitから復元

GitからUnityのプロジェクトを復元しました。
そしてバックアップしていたその後の編集内容を手動でマージ....
動いた!

まとめ

  1. いろいろやってみて解決しなかったらGitから復元しよう
  2. 慣れないことをする前にはバックアップを取ろう

おそらく原因は「Il2cpp⇆Mono2x」だとは思いますが戻しても動かなくなるのは厄介ですね...

【Unity】XcodeのプロジェクトからTARGETSを消してはならない!

発生した問題

UnityでiOS向けにBuildしてXcodeのプロジェクトに変換したときに以下のエラーが出てビルドできない。

Exception: Deletion of either of the "Unity-iPhone" or "Unity-iPhone Tests" targets is not supported

原因

Xcode側でTARGETSから「Unity-iPhone Tests」を消していた。
※「Unity-iPhone」「Unity-iPhone Tests」は残しておき、名前も変えない。

解決策

UnityでFileからBuild Settingsを選び、BuildかBuild And Runでファイルを選択するときに出てくるWindowで「Replace」を選択する。
f:id:nanokanato:20170728134716p:plain:w300

Append(追記)じゃなくReplace(置換)することでTARGETSを作成してくれる。
しかし、Xcode側から設定した内容が消えているので注意です。

【GAS】GoogleAppScriptをはじめよう

GoogleAppScriptとは

Google Apps Script(GAS)はGoogleが提供する11のサービスをクラウド上でスクリプトを実行することで操作できるサービスです。
言語はJavaScriptをベースとしています。

例えば一定時間ごとにデータを集計したり、SNSに投稿するBotを作ったり、GoogleSpleatSheetを編集したりなどが可能です。

以下のGoogleの機能と標準で連携することができるのでいろいろな使い方が可能です。

  • カレンダー
  • コンタクト
  • ドライブ
  • ドキュメント
  • スプレッドシート
  • フォーム
  • Gmail
  • グループ
  • マップ
  • サイト
  • Languages

GASを用意しよう

GoogleDriveに入って右クリックで「その他 > Google Apps Script」を選択。
※その他に無い場合はアプリを追加で探してみてください。
f:id:nanokanato:20170725121811p:plain:w300

GASを使ってみよう

上のアプリを追加で作成された無題のプロジェクトです。
左上の「無題のプロジェクト」の部分をクリックすることでプロジェクト名を変更できます。
f:id:nanokanato:20170725122209p:plain:w300

左の「コード.gs」の部分にgsファイルが並びます。
ファイル名の右の▽をクリックすることでファイル名を変更したり、ファイルを削除、コピーできます。
f:id:nanokanato:20170725122915p:plain:w200

とりあえず動かそう

プロジェクト名、ファイル名、メソッド名を「test」に変えてみました。
f:id:nanokanato:20170725130009p:plain:w300

function test() {
    Logger.log("Hello World!");
}

Logger.log(string)でログを表示します。

f:id:nanokanato:20170725131256p:plain:w16ボタンをクリックで実行されます。

初回実行時のみ、Googleアカウントの認証が必要です。
f:id:nanokanato:20170906121531p:plain:w300

実行後「⌘+Enter(Mac)」でログが表示されます。

f:id:nanokanato:20170725130553p:plain:w300


行数の部分をクリックすることでブレークポイントを設定できます。
f:id:nanokanato:20170725131350p:plain:w16ボタンを押すことでブレークポイントで停止してくれます。
f:id:nanokanato:20170725130702p:plain:w300
2017/07/25現在ではブレークポイントで止まってくれるのは実行開始したファイル内だけのようです。
別のファイルのブレークポイントで止めたい場合は、メソッドから別のファイルのメソッドを呼び出しているところで一度止めてから、f:id:nanokanato:20170725131414p:plain:w16ステップインすることで呼び出し先の別ファイルのメソッドへ移動できます。
別のファイルに移動した後であればそのファイルのブレークポイントに止まってくれました。

他に何ができるかは以下を参考にどうぞ
Calendar Service  |  Apps Script  |  Google Developers

トリガーの設定

定期的に集計などをしたい場合、その都度再生ボタンを押すのは面倒です。
トリガーとは再生ボタンを押さなくても一定時間ごとに指定したメソッドを実行してくれます。
f:id:nanokanato:20170725131906p:plain:w16ボタンを押して、トリガーを設定してください。

トリガーを設定するウィンドウが表示されました。
最初は青文字のリンクを押すことでトリガーを追加できます。
f:id:nanokanato:20170725132009p:plain:w300

実行の部分はメソッド名が入ります。ここではtestを選んでいます。
イベントには実行周期を登録します。
「特定の日時」「月タイマー」「週タイマー」「日タイマー」「時タイマー」「分タイマー」から選べます。
また、「新しいトリガーを追加」で新しいトリガーを追加できます。
f:id:nanokanato:20170725132339p:plain:w300

時タイマーで設定して1時間ごとにはなりますが、0:16→1:15のように0分ぴったりでは無いので注意です。

ぴったりの時間に処理したい場合は以下のように毎分に設定し、NowMin(分)が0であることを確認すれば間違いないです。
これで毎時0分ぴったりに処理することになります。

    var Nowymdhms = new Date();
    var NowYear = Nowymdhms.getYear();
    var NowMon = Nowymdhms.getMonth() + 1;
    var NowDay = Nowymdhms.getDate();
    var NowHour = Nowymdhms.getHours();
    var NowMin = Nowymdhms.getMinutes();
    if (NowMin == 0) {

    }

【Unity】パズドラ風パズル作ってみた

はじめに

最近いろんなゲームをやりつつ、戦略系などが好きなんだなーと実感しました。
その中でも単純なんだけど考えるようなものは暇つぶしには最適で、それがパズルでした。

パズルと言ったらパズドラということもあり(1週間くらいしかやったことないが...)
作ってみることにしました。

完成した動作が以下です。
レイアウト等はまだまだですが一応パズルできる...という感じです。
これにコンボやスコアを設定すれば完璧、作り上簡単にカスタマイズできるはずです。


実装内容

実装後に違うパズルに改造してしている途中なので確かこうだったなぁという復元になってますが、動かない場合はコメント等にておしらせください。

Line3Block

パズルのピースを用意します。
80x80のPrefabです。
f:id:nanokanato:20170616113854p:plain:w300

階層構造はBlock(CreateEmpty:空のGameObject)に
BackGround(UI.Image)とIcon(UI.Image)を用意します。
f:id:nanokanato:20170616113815p:plain:w300

Blockには以下のScriptを追加します。
主にピースの移動後の座標と種類を管理しています。

InspectorからBlockの色(BlockColors)とキャラアイコン(BlockCharas)を設定します。
BackGroundとIconにはBlockの下層に作った同じ名前のUI.Imageを設定します。
f:id:nanokanato:20170616114234p:plain:w300

設定したBlockの色とキャラアイコンの数だけピースの種類が増えます。

Line3Puzzle

BlockのPrefabを生成したり、ピースが揃っているか確認する部分です。

階層構造はCanvas以下にBoard(CreateEmpty:空のGameObject)を配置しているだけです。
Canvasは640x1136で、Boardは560x760です。
Camera等の設定は省きます。
f:id:nanokanato:20170616114904p:plain:w300

Canvasには以下のScriptを追加します。
Blockの生成と破棄、タップ操作の管理、ピースが揃ったかどうかの確認、アニメーションなどを行なっています。

InspectorのBoardにはCanvas下層のBoardを、BlockPrefabにはBlockを設定してください。
f:id:nanokanato:20170616115225p:plain:w300

ビルド

あとはビルドすることでCanvas > BoardにBlockが生成され、操作待ちになります。

処理の手順は以下になります。

  1. ピースの生成 → 2
  2. 揃っているか確認
    揃っていた場合 → 3
    揃っていない場合 → 6
  3. 揃っていたピースを消す → 4
  4. ピースを下に詰める
    空いている場所には上から新たなピースを落とす → 5
  5. 移動後に再確認 → 2
  6. 移動するピースの選択待ち → 7
  7. ピースを移動して入れ替える
    指が離されたら移動を確定 → 2

まとめ

パズルって単純なゲームなのに内部では色々なロジックが動いており面白いです。
結構出尽くしている感もありますがぜひオリジナルのパズルを作ってみてください。

UnityのAssetBundleを簡単にしてみた

はじめに

AssetBundleを使用するときにAssetBundleを読み込んでローカルに保存、Spriteの取得というのが毎回実装していると手間なので以下のように呼び出せるようにしました。
f:id:nanokanato:20170615115801p:plain:w300

AssetBundleMan


キャッシュ→StreamingAssets→ローカルの保存場所→サーバーの順にAssetBundleを探しに行きます。
一度取得したAssetBundleはキャッシュされます。

サーバーとローカルへの通信はWWWクラスを使用したWWWManを使っています。
madgenius.hateblo.jp

使用方法

Spriteを取得する場合
AssetBundleMan.GetAssetBundleSprite("AssetBundle名", "ファイル名.png", (Sprite sprite) => {
    image.sprite = sprite;
});

取得失敗の可能性もあるのでnullチェックしておくとベストです。
AssetBundleとSpriteの取得後に呼ばれて画像が反映されます。

GameObject,Prefabを取得する場合
AssetBundleMan.GetAssetBundleGameObject("AssetBundle名", "ファイル名.prefab", (GameObject prefab) => {
    GameObject gameObject = Instantiate (prefab, this.transform);
});

取得失敗の可能性もあるのでnullチェックしておくとベストです。
AssetBundleとGameObjectの取得後に呼ばれてPrefabから生成されます。

UnityのWWW通信をCallBackで受けとるクラス

簡単にメモ程度ですが、WWWでの通信を簡単に行うために作成しました。
以下の画像の通り、Coroutineを使わずにCallBackでProgress(進捗度)と通信結果を受け取ることができます。
f:id:nanokanato:20170608185914p:plain:w300

staticのクラスなのでどこからでも呼び出すことができます。
MonoBehaviourを通信開始時に取得してCoroutineの処理をしているのでその部分のみ指定のMonoBehaviourに差し替えて使用することもできます。
現状、タイムアウトの処理がないので今後実装するかもしれません。