読者です 読者をやめる 読者になる 読者になる

Λάδι Βιώσας

http://profile.hatena.ne.jp/kenkitii/

サンシャイン牧場用のミクシーアプリを作ってみた

最近、ミクシーサンシャイン牧場ってゲームにハマっているんですが、その補助ツールを作りたくなり、ミクシーアプリの作り方を勉強してみたので、その記録をメモ。あれこれ検索したところ、以下の手順で作れました。

3ステップで出来るミクシーアプリ

1.ここデベロッパー登録する。
2.レンタルサーバーに以下のようなファイルを test.xml などにしてアップロードする。

<?xml version="1.0" encoding="UTF-8" ?>  
<Module>  
<ModulePrefs title="任意のタイトル">  
<Require feature="opensocial-0.8" />  
</ModulePrefs>  
<Content type="html">  
<![CDATA[  
hello world!!!(←ココの部分が表示されます)
]]>  
</Content>  
</Module>  

3.ここからアプリ登録して、こんな感じで↓URLを開いて、「hello world!!!」って出ればオッケーです。

http://mixi.jp/run_appli.pl?id=(あなたのアプリケーションID)&nocache=1

お手軽ですね!!ちなみに自分が作ったサンシャイン牧場ミクシーアプリはココから登録できます。どうぞご利用ください。

で、作ったものとか

以下にソースコードを公開しときます。Javascript の書き方よくわかってなくて汚いと思いますが、ご了承ください。

<?xml version="1.0" encoding="UTF-8" ?> 
<Module>
  <ModulePrefs title="サン牧成熟シミュレーター"> 
    <Require feature="opensocial-0.8"/>
  </ModulePrefs>
  <Content type="html">
  <![CDATA[
<style type="text/css">
table.sunboku{
    border-top:1px solid #663300;
    border-left:1px solid #663300;
    border-collapse:collapse;
    border-spacing:0;
    background-color:#ffffff;
    empty-cells:show;
}
.sunboku th{
    border-right:1px solid #663300;
    border-bottom:1px solid #663300;
    color:#330000;
    background-color:#996633;
    background-image:url(http://rocky.sakura.ne.jp/mixiapp/table-back.gif);
    background-position:left top;
    padding:0.3em 1em;
    text-align:center;
}
.sunboku td{
    font-size: small;
    text-align: center;
    border-right:1px solid #663300;
    border-bottom:1px solid #663300;
    padding:0.3em 1em;
    line-height: 1;
}
</style>
<script type="text/javascript">
// サンシャイン牧場成熟時間ハッシュデータ
var hashSunboku = {
    'ニンジン': [0.75,0,0],
    'かぼちゃ': [7.50,0,0],
    'トウモロコシ': [9.00,0,0],
    'トマト': [10.50,0,0],
    'ナス': [13.00,0,0],
    'りんご': [15.50,24.93,0],
    'メロン': [17.00,0,0],
    'いちご': [24.50,35.53,46.56],
    'ブドウ': [27.00,39.15,0],
    '金糸瓜': [29.00,42.05,55.1],
    '豌豆': [32.50,47.13,0],
    'マンゴ': [33.50,48.58,63.66],
    'クワの実': [44.50,64.53,0],
    'レモン': [45.50,65.97,86.44],
    'レイシ': [46.00,66.7,0],
    'メキシコシティー': [47.00,68.15,89.3],
    'パラミツ': [48.00,69.6,0],
    '柿': [49.00,71.05,93.1],
    'ハエトリ草': [50.50,73.23,0],
    '桃': [51.00,73.95,96.9],
    'バナナ': [52.00,75.4,0],
    'サクランボ': [54.00,78.3,102.6],
    '蜜柑': [64.00,92.8,0],
    'リュウガン': [65.00,94.25,123.5],
    '紅ナツメ': [66.00,95.7,0],
    'シュガーケーン': [67.00,97.15,127.3],
    'タンポポ': [50.00,0,0],
    'センノウ': [51.00,0,0],
    'カラタネオガタマ': [52.00,84.65,0],
    'ザクロ': [53.00,86.25,0],
    'ハリエンジュ': [54.00,86.94,119.88],
    '金盞花': [56.00,90.16,124.32],
    '金魚草': [58.00,93.38,128.76],
    'バイオレット': [22.00,0,0],
    'ユリ': [20.23,0,0]
};
var n = 10;

//
// n時間後の日付時刻を求める
//
// 日付型と加算時間からn時間後の日付を求める関数
// date 日付型
// addDays 加算日。マイナス指定でn日前も設定可能
//
function calcDate(date, addHours) {
    var dt = new Date();
    dt.setTime(date);
    var baseSec = dt.getTime();
    var addSec = addHours * 3600000; //時間数 * 時間のミリ秒数
    var targetSec = baseSec + addSec;
    dt.setTime(targetSec);
    return dt;
}

// Date型を YYYY/mm/dd HH:MM:SS の文字列にする関数
function formatDate(date) {
    var dt = date.getFullYear() + "/" + zeroUme(date.getMonth() + 1) + "/" 
        + zeroUme(date.getDate()) + " " + zeroUme(date.getHours()) + ":" 
        + zeroUme(date.getMinutes()) + ":" + zeroUme(date.getSeconds());
    return dt;
}

// 二桁の数値をゼロ埋めで文字列化する関数
function zeroUme(num) {
    var len = 2;
    var sa = len - (num+"").length;
    var add0 = "";
    if (sa > 0) for (var i=0; i<sa; i++ ) {add0 += "0"; }
    return (add0 + num);
}

// 作物成熟時間の表示
function dispRipenessTime(id, name, time) {
    if (name == "") {
	return 0;
    }

    // 作物の名前から成熟時間を得る
    // var key = document.getElementById(id).innerHTML;
    var ts = hashSunboku[name];

    // 文字列をパースして日付を得る
    time = Date.parse(time);
    var date = new Date();
    date.setTime(time);

    var count = 1;
    for (var i=0; i < ts.length; i++) {
        var t = ts[i];
        count += 1;
        if (t == 0) {
            document.getElementById(id+"time"+count).innerHTML = "-";
        } else {
            var dt = calcDate(date, t);
            document.getElementById(id+"time"+count).innerHTML = formatDate(dt);
        }
    }
}

// 「植えた」ボタンの処理
function plant(id) {
    var now = new Date;
    var obj = document.getElementsByName(id+"combo")[0];
    var idx = obj.selectedIndex;
    var name = obj.getElementsByTagName("OPTION")[idx].firstChild.nodeValue;

    document.getElementById(id).innerHTML = name;
    document.getElementById(id+"time1").innerHTML = formatDate(now);
    dispRipenessTime(id, name, formatDate(now)); // 成熟時間の表示    

    buttonToggle(id, true); //「植えた」ボタンを非表示
    savePlant(id); // 保存
}

// ボタン、セレクトボックスの表示・非表示処理
function buttonToggle(id, flag) {
    var obj = document.getElementsByName(id+"combo")[0];
    obj.style.display = (flag==true)? "none" : "inline";
    document.getElementById(id+"btn1").style.display = (flag==true)? "none" : "inline";
    document.getElementById(id+"btn2").style.display = (flag==true)? "inline" : "none";
}

// 保存処理
function savePlant(id) {
    // 作物の名前を得る
    var name = document.getElementById(id).innerHTML;
    name = escape(name);

    // 日付を得る
    var time = document.getElementById(id+"time1").innerHTML;
    time = escape(time);

    var req = opensocial.newDataRequest();
    req.add(req.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, id, name));
    req.add(req.newUpdatePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, id+"time1", time));
    req.send(function(response) {
      if (response.hadError()) {
          alert(response.getErrorMessage());
      } else {
	  debugprint("保存しました"+id);
      }
    });
}

// 「収穫」ボタンの処理
function reset(id) {
    document.getElementById(id).innerHTML = "";
    document.getElementById(id+"time1").innerHTML = "";
    document.getElementById(id+"time2").innerHTML = "";
    document.getElementById(id+"time3").innerHTML = "";
    document.getElementById(id+"time4").innerHTML = "";

    buttonToggle(id, false); // 収穫ボタンを非表示
    savePlant(id); // 保存
}

// 保存した作物名・時刻を取得して表示する
function loadData() {
    var req = opensocial.newDataRequest();
    var fields = new Array();
    for (var i=0; i < n; i++ ) {
        fields[i*2]   = "plant" + (i+1);
        fields[i*2+1] = "plant" + (i+1) + "time1";
    }
    req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), "viewer");
    req.add(req.newFetchPersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, fields), "viewer_data");
    req.send(function(response) {
		 if (response.hadError()) {
		     alert(response.getErrorMessage());
		 } else {
		     var myId = response.get("viewer").getData().getId();
		     var data = response.get("viewer_data").getData();
		     for (var i=0; i<n; i++) {
			 var name = unescape(data[myId][fields[i*2]]);
			 var time = unescape(data[myId][fields[i*2+1]]);
			 if (name != "undefined" && name !="") {
			     document.getElementById(fields[i*2]).innerHTML = name;
			     document.getElementById(fields[i*2+1]).innerHTML = time;
			     dispRipenessTime(fields[i*2], name, time);
			     buttonToggle(fields[i*2], true);
			 } else {
			     buttonToggle(fields[i*2], false);
			 }
		     }
		 }
	     });
}

function createTable() {
    var str = '';
    for (var i=1; i < n+1; i++) {
        str += '<tbody>';
        str += '<tr>';
        str += '<td align="right">' + i + '</td>';
        str += '<td><div id="plant' + i + '"></div></td>';
        str += '<td><div id="plant' + i + 'time1"></div></td>';
        str += '<td><div id="plant' + i + 'time2"></div></td>';
        str += '<td><div id="plant' + i + 'time3"></div></td>';
        str += '<td><div id="plant' + i + 'time4"></div></td>';
	str += '<td><select name="plant' + i + 'combo">';
        for (var key in hashSunboku) {
            str += '<option value="' + hashSunboku[key] + '">' + key + '</option>';
        }
        str += '</select><button id="plant'+i+'btn1" onclick="plant(\'plant' + i + '\')">植えた!</button>'+
	    '<button id="plant'+i+'btn2" onclick="reset(\'plant' + i + '\')">収穫</button></td>';
        str += '</tr>';
        str += '</tbody>';
    }
    document.write(str);
}
</script>

<body onload="loadData()">

<table class="sunboku">
<tr><th>No.</th><th>種類</th><th>植えた日時</th><th>成熟日時</th><th>二期日時</th><th>三期日時</th><th>操作</th></tr>
<script language="javascript" type="text/javascript">createTable();</script>
</table> 
成熟時間データは、<a href="http://spreadsheets.google.com/pub?key=t8nRmfi-RMX7pCrdMDeKYSw&gid=0">こちら</a>のものを利用させてもらっています。サンクス!
</body>
  ]]> 
  </Content>
</Module>

ハマッたこと

ミクシーアプリじゃなくて、javascript の話ですけど、IE で document.write 等を用いて、テーブルを動的に作成する場合、TBODY タグを入れないと表示されなくてハマりました。firefox だと問題ないんですけどね、、、。