2015年2月15日日曜日

ブラウザとNode.jsで動作するライブラリの書いたときのやり方

動機

gifken というJavaScriptでGIF画像を作ったり、GIFアニメを分割や逆再生するようなライブラリをちまちま書いています。
もともとブラウザで動作させることしか考えていなかったのですが、Node.jsでも動かせそうだったので、対応させたときのやり方を書きます。
(ただNode.js環境でGIF画像など扱いたい場合は http://aheckmann.github.io/gm/ などでGraphicsMagickやImageMagick使った方が素直だし速度面などを考えても良いです)

導入

もっと良い方法がありそうな気もします……

もともとのものが1つの大きなJavaScriptだったので、ブラウザ依存の処理と、共通で利用できる処理をまず分けました。
Node.jsにはモジュールの仕組みが既にあり、ブラウザでその処理を動くようにするソフトウェアが既にあるのでそれを利用するのが楽でしょう。(Browserify, Webpackなど)
私は Browserify を利用しました。

Browserify の導入自体は楽だったのですが、Browserifyはrequireなどの依存関係を解決してJavaScriptファイルを書き出してくれますが、Browserifyで生成した関数に閉じた状態で出力されるので、scriptタグで読み込むだけで使えるようになる、という状態にはなりません。
Browserify のオプションを見ても特にそれらしいものは見つかりませんでした。
よって、以下のようにしました。

windowオブジェクトに追加してしまいます。
WebWorkerではwindowがglobal objectではないので、importScriptsする前に var window = self; とでもしてください……

まとめ


Browserifyの導入とglobal objectへの追加で何とかなった。

2014年4月10日木曜日

Vert.xでScalaのサンプルをとりあえず動かす

Vert.x が最近気になっているので、サンプルを動かすところまでやってみたいと思います。
Javaのサンプルでもいいのですが、多言語対応がウリの1つで、Scalaでも書けるということなので、せっかくだからScalaを使ってみたいと思います。

使用する Vert.x のバージョンは現在最新の 2.1RC3 を使います。

Vert.x のインストール


公式サイトの手順を参考に進めます。

2.1RC3のダウンロード後、解凍する。
$ tar -zxf ~/Downloads/vert.x-2.1RC3.tar.gz
$ ~/vert.x-2.1RC3/bin/vertx version
2.1RC3 (built 2014-04-02 10:07:26)

Scala モジュールのインストール


2.1RC3の時点では、 Scala は標準では使えず、別途言語モジュールとして提供されているようです。
詳しくは知らないですが、 Vert.x Module Registry に
io.vertx~lang-scala~1.0.0
が登録されているので、どうやらこれのようです。

公式のモジュールマニュアルを参考にインストールしてみます。

$ ~/vert.x-2.1RC3/bin/vertx install io.vertx~lang-scala~1.0.0
Attempting to install module io.vertx~lang-scala~1.0.0 
Downloading io.vertx~lang-scala~1.0.0. Please wait... 
Downloading 100%
Module io.vertx~lang-scala~1.0.0 successfully installed 
Succeeded in installing module

どうやらインストールできたっぽい。

サンプルの実行


Vert.x ExamplesとしてサンプルがGitHubに公開されているので、 Scala の Route Match を動かしてみたいです。
$ git clone https://github.com/vert-x/vertx-examples.git
$ cd vertx-examples/src/raw/scala/
$ ~/vert.x-2.1RC3/bin/vertx run route_match/RouteMatchExample.scala
Failed in deploying verticle 
java.lang.ClassNotFoundException: route_match/RouteMatchExample.scala
 at org.vertx.java.platform.impl.ModuleClassLoader.loadFromModule(ModuleClassLoader.java:127)
 at org.vertx.java.platform.impl.ModuleClassLoader.loadClass(ModuleClassLoader.java:108)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
 at org.vertx.java.platform.impl.java.JavaVerticleFactory.createVerticle(JavaVerticleFactory.java:55)
 at org.vertx.java.platform.impl.DefaultPlatformManager$21.run(DefaultPlatformManager.java:1723)
 at org.vertx.java.core.impl.DefaultContext$3.run(DefaultContext.java:175)
 at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:370)
 at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:353)
 at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
 at java.lang.Thread.run(Thread.java:724)

Failed in deploying verticle 
java.lang.ClassNotFoundException: route_match/RouteMatchExample.scala
 at org.vertx.java.platform.impl.ModuleClassLoader.loadFromModule(ModuleClassLoader.java:127)
 at org.vertx.java.platform.impl.ModuleClassLoader.loadClass(ModuleClassLoader.java:108)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
 at org.vertx.java.platform.impl.java.JavaVerticleFactory.createVerticle(JavaVerticleFactory.java:55)
 at org.vertx.java.platform.impl.DefaultPlatformManager$21.run(DefaultPlatformManager.java:1723)
 at org.vertx.java.core.impl.DefaultContext$3.run(DefaultContext.java:175)
 at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:370)
 at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:353)
 at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
 at java.lang.Thread.run(Thread.java:724)
module のインストールがうまくいってない気がしてくるエラーです。
http://vertx.io/mods_manual.html をもう少し丁寧に読んでみると、 lang-impl に書いてあることが参考になりそうでした。
~/vert.x-2.1RC3/conf/langs.properties を覗くと
groovy=io.vertx~lang-groovy~2.0.0-final:org.vertx.groovy.platform.impl.GroovyVerticleFactory
のような記述があり、これにならって Scala の設定も書けば動きそうな気がしてきます。

# ~/vert.x-2.1RC3/conf/langs.properties に以下を追加
scala=io.vertx~lang-scala~1.0.0:org.vertx.scala.platform.impl.ScalaVerticleFactory
.scala=scala

再度サンプルを実行。
$ ~/vert.x-2.1RC3/bin/vertx run route_match/RouteMatchExample.scala
Compiling route_match/RouteMatchExample.scala as Scala script 
Starting route_match/RouteMatchExample.scala 
Succeeded in deploying verticle
うまく実行されたので、 localhost:8080 を開いて動作を確認するところまでできました。


とりあえず動かすとこまでできたので、次はパフォーマンスの検証などしてみたいです。

2014年3月2日日曜日

画像に文字を上書きできるようなものを作ってみた

WEBにある画像の文字だけ書き換えたいなーってときに、一度保存してからペイントで編集して〜っていう作業が面倒だと思ったので、それっぽいものを作ってみた。


実は1年くらい前から公開してたのですが、放置してました。
デザインももっとマシにしようとは思いつつも手をつけてないので察してください。

どういうことができるかというと、

URLのパラメータをぽちぽち変更するのは大変だったので、JavaScriptで簡単なURLメーカーもつくりました。

機能しょぼいですが、クソ文字コラメーカーぐらいにはなるのかなーという気はします。
Twitter Cardsにも対応させた気がするので、Twitterでの画像展開もされるかも。

2013年10月6日日曜日

JavaScriptでGIFアニメの分解と逆再生やってみたかった

ネット上に転がっているGIFアニメ、とても好きなのですが、フレーム早すぎてなにが写っているかわからないものもあったりでフレーム毎に分割したいと思うことが何度かありました。
ImageMagickとかで分割とかはとても楽にできますが、貧弱なサーバーだとちょっとアクセス来ただけで残念なことになってしまうので、クライアント側でできないのかなってことでJavaScriptで実行してみることにした。

一応それなりに動くところまでできたので公開します。
現在Chrome拡張機能だけです。Firefoxはそのうち……

ソース&説明: https://github.com/aaharu/gifken/tree/master/sample/chromeextension
ダウンロード: https://www.dropbox.com/s/gkrl4scag4hf2tt/chromeextension.crx

実際使うとどんな感じになるのか?
こんな感じになります。

GifgigのとあるGIFアニメ。うまくできた&わかりやすい画像選んでみました。
岐阜gazo - まどマギ


右クリックで「GIFアニメ分解」を選択


新しいタブで結果画像が表示される


ArrayBufferでバイナリ解析できるので、それで実装しています。バイナリ解析行っているところはTypeScriptで実装してみました。
不完全なので、うまくいかない画像あったら教えてください。

2013年5月11日土曜日

EaselJS (CreateJS) を拡張してTextで縦書きできるようにしてみた

最近EaselJSをさわりはじめたんですが、AS3を少しやっていたのでAPIがにていてわかりやすいです。
そんなに複雑なもの作ろうとしているわけじゃないので、別に素のJSをつかってかいても良かったんですが、、、

EaselJSのTextですが、例によって縦書きはできないようで、縦書きをたまに使用する日本人にはかゆいところに手が届かない感じです。
EaselJSはMITライセンスのOSSだったので、勉強がてら拡張して縦書きもできるTextExを作ってみました。

TextEx.js

変更点はdirectionプロパティを追加して、directionが"vertical"のときforで一文字ずつ縦に描画するようにしただけです。
--- Text.js 2013-05-11 19:36:31.000000000 +0900
+++ TextEx.js 2013-05-11 20:57:56.000000000 +0900
@@ -41,7 +41,7 @@
  * multiple font styles, you will need to create multiple text instances, and position them manually.
  *
  * <h4>Example</h4>
- *      var text = new createjs.Text("Hello World", "20px Arial", "#ff7700");
+ *      var text = new createjs.TextEx("Hello World", "20px Arial", "#ff7700");
  *      text.x = 100;
  *      text.textBaseline = "alphabetic";
  *
@@ -59,17 +59,17 @@
  * @param {String} [color] The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex.
  * "#F00", "red", or "#FF0000").
  **/
-var Text = function(text, font, color) {
+var TextEx = function(text, font, color) {
   this.initialize(text, font, color);
 }
-var p = Text.prototype = new createjs.DisplayObject();
+var p = TextEx.prototype = new createjs.DisplayObject();
 
  /**
   * @property _workingContext
   * @type CanvasRenderingContext2D
   * @private
   **/
- Text._workingContext = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")).getContext("2d");
+ TextEx._workingContext = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")).getContext("2d");
 
 // public properties:
  /**
@@ -142,6 +142,13 @@
   **/
  p.lineWidth = null;
  
+ /**
+  * Indicates the text direction. Any of "horizontal" or "vertical". Default is "horizontal".
+  * @property direction
+  * @type String
+  */
+ p.direction = "horizontal";
+ 
 // constructor:
  /**
   * @property DisplayObject_initialize
@@ -182,7 +189,7 @@
  p.DisplayObject_draw = p.draw;
  
  /**
-  * Draws the Text into the specified context ignoring it's visible, alpha, shadow, and transform.
+  * Draws the TextEx into the specified context ignoring it's visible, alpha, shadow, and transform.
   * Returns true if the draw was handled (useful for overriding functionality).
   * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
   * @method draw
@@ -226,7 +233,7 @@
 
  /**
   * Returns the approximate height of multi-line text by multiplying the number of lines against either the
-  * <code>lineHeight</code> (if specified) or {{#crossLink "Text/getMeasuredLineHeight"}}{{/crossLink}}. Note that
+  * <code>lineHeight</code> (if specified) or {{#crossLink "TextEx/getMeasuredLineHeight"}}{{/crossLink}}. Note that
   * this operation requires the text flowing logic to run, which has an associated CPU cost.
   * @method getMeasuredHeight
   * @return {Number} The approximate height of the drawn multi-line text.
@@ -236,12 +243,12 @@
  }
  
  /**
-  * Returns a clone of the Text instance.
+  * Returns a clone of the TextEx instance.
   * @method clone
-  * @return {Text} a clone of the Text instance.
+  * @return {TextEx} a clone of the TextEx instance.
   **/
  p.clone = function() {
-  var o = new Text(this.text, this.font, this.color);
+  var o = new TextEx(this.text, this.font, this.color);
   this.cloneProps(o);
   return o;
  }
@@ -252,7 +259,7 @@
   * @return {String} a string representation of the instance.
   **/
  p.toString = function() {
-  return "[Text (text="+  (this.text.length > 20 ? this.text.substr(0, 17)+"..." : this.text) +")]";
+  return "[TextEx (text="+  (this.text.length > 20 ? this.text.substr(0, 17)+"..." : this.text) +")]";
  }
  
 // private methods:
@@ -266,7 +273,7 @@
 
  /** 
   * @method cloneProps
-  * @param {Text} o
+  * @param {TextEx} o
   * @protected 
   **/
  p.cloneProps = function(o) {
@@ -284,7 +291,7 @@
   * @protected 
   **/
  p._getWorkingContext = function() {
-  var ctx = Text._workingContext;
+  var ctx = TextEx._workingContext;
   ctx.font = this.font;
   ctx.textAlign = this.textAlign||"start";
   ctx.textBaseline = this.textBaseline||"alphabetic";
@@ -333,16 +340,28 @@
  /** 
   * @method _drawTextLine
   * @param {CanvasRenderingContext2D} ctx
-  * @param {Text} text
+  * @param {String} text
   * @param {Number} y
   * @protected 
   **/
  p._drawTextLine = function(ctx, text, y) {
   // Chrome 17 will fail to draw the text if the last param is included but null, so we feed it a large value instead:
+  if (this.direction === "vertical") {
+   var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
+   if (this.outline) {
+    for (var i=0, l=text.length; i<l; ++i) {
+     ctx.strokeText(text.charAt(i), -y - lineHeight, i*lineHeight, this.maxWidth||0xFFFF);
+    }
+   } else {
+    for (var i=0, l=text.length; i<l; ++i) {
+     ctx.fillText(text.charAt(i), -y - lineHeight, i*lineHeight, this.maxWidth||0xFFFF);
+    }
+   }
+  } else {
    if (this.outline) { ctx.strokeText(text, 0, y, this.maxWidth||0xFFFF); }
    else { ctx.fillText(text, 0, y, this.maxWidth||0xFFFF); }
-  
+  }
  }
 
-createjs.Text = Text;
-}());
\ No newline at end of file
+createjs.TextEx = TextEx;
+}());

2012年1月22日日曜日

きんつば

今日は上野・浅草あたりをぶらぶらしてきたんですが、お店で「きんつば」を見つけたので買ってみました。


この「きんつば」というお菓子、Wikipediaにも書かれてますが、私の出身のほう(福島県会津)では、今川焼き(大判焼き)のことをきんつばと呼んでたりします。
http://ja.wikipedia.org/wiki/%E3%81%8D%E3%82%93%E3%81%A4%E3%81%B0

各地で呼び方が異なるならまだしも、その名前の別のお菓子があるとなると非常に紛らわしいです。罠です。陰謀です。

地元の人は今川焼きじゃないきんつばのこと知らない人が多いんじゃないかと思います。私も知ったのは比較的最近(大学生の時)で、友人に話しても「え?マジで?」みたいな反応です。
会津でも場所によると思いますが、喜多方、若松あたりはきんつばって呼んでる?と思うので、ほぼ会津全域そうなんじゃないかなー。
中通りになるとどうだかしらないですけど。

2012年1月8日日曜日

Flashで3Dなお絵描き

大学の時にブラウザ上で3Dモデルに絵を描くアプリケーションを作りました。

長い間ブログを放置していたので、それの紹介でもしたいと思います。

まずブラウザで3Dを扱うというのはあまりない話です。重いですし。
下のリンクでは描いたテクスチャを3Dモデルに貼り付けるようなことをやっていました。
http://labs.logosware.com/archives/21

なんやかんやあってFlashで作るのがいいだろうって話になって、そこからは自分がいろいろ試して作っていくことになりました。
Flashもそんな詳しいわけではなかったし、3Dは全く分からないというぐらいだったので開発には苦労しました。
ブログの過去記事などを見ると試行錯誤の跡が少し見られると思います。

まず3Dモデルをどうするかという話でしたが、開発メンバーが有料のモデリングソフトもってない、Flashの3Dライブラリでサポートされてるといった点などを考えた結果、Collada(.xml)形式にしました。

で、3Dライブラリは当時(今でも?)一番メジャーなPapervision3Dにしようと考えていました。

http://blog.r3c7.net/?p=333
上の記事を参考にサンプルを作っていきました。3Dモデルの作成や、マウスイベントなど苦労しましたがなんとかそれっぽいものはできました。
じゃあ次はシェーダーを試そうと思った時に問題が起きました。
http://aaharu.blogspot.com/2009/10/pv3duv.html
UVマッピングを使用したモデルにシェーダーを適用すると黒い線が出てきました。
これの解決方法は結局わからず、Papervision3D以外のライブラリではどうなのか試してみることにしました。

で、試したのがAway3Dです。
まあFlashの3Dライブラリといってもメジャーなのは、Papervision3DとAway3DとAlternativa3Dぐらいでしたし、Papervision3Dから派生したものだと聞いて今までのが移植しやすいかなーと。

それで試した結果、Away3Dは問題なく動作することがわかり、3DライブラリはAway3Dを使用することに決めました。
Away3Dにして良かった点はいくつかありました。
・上の黒い線の問題が解決したこと
・3Dマウスイベントが豊富なこと
・シェーダーなども豊富で、きれいであったこと
絵を描くということを考えると3Dのオブジェクトのマウスイベントがあるのはとてもよかったです。
悪いと思った点もいくらかあります。
・重い
・そんなに移植しやすいわけではなかった

とそんな感じでぐだぐだと作りました。
せっかくなので、FlashDevelopで書き直した、一部機能削除版をソースコードごと公開します。(開発時はFlashBuilderを使いました。学生だったので無料ライセンスで)
今見るといろいろと直したくなりますがそこらへんは許してやってください。


Flexアプリ http://www.aaharu.com/data/UruLabCreater.swf
ソースコード(Flex) http://www.aaharu.com/data/srcview_urulabcreater/index.html
ソースコード(ライブラリ)http://www.aaharu.com/data/srcview_urulab/index.html

ソースコード書き出し後に修正行ってるのでソースコード最新じゃないですけどだいたいあってます。

2011年8月10日水曜日

GAE/Pのtemplateのextendsでちょっとはまった

Google App Engine (Python) のdjangoのテンプレートを使っていてちょっと躓いたことがあった。

私は、 http://www.aaharu.com/ というサイトを持っていますが、これはGAEで作っていて、DNSもGoogle Appsで設定しています。
お金がかかっているのはドメインだけ。

以前まではテンプレートを使わないでHTMLを表示していたわけですが、更新のたびにすべてのHTMLを変更するのが面倒になったので、テンプレートを使ってみることにしました。
GAEには標準でDjangoのテンプレートエンジンが入っていて、テンプレート継承の仕組みがあります。
http://djangoproject.jp/doc/ja/1.0/topics/templates.html
http://d.hatena.ne.jp/griefworker/20091028/gae_inherit_template

それで、テンプレート継承を使ってみたわけなのですが、HTMLの表示がくずれる現象が起きました。
HTMLのソースを表示させても問題は見当たらない。そしてなぜかOperaだけは正しく表示してくれる。

Chromeの『要素を検証』で確認してみると、どうやらbase.htmlの内容の前に空文字が挿入されているようだったので原因をいろいろ調べると、原因はUTF-8のBOMだった。
base.htmlがBOMありのUTF-8だったため、先頭の1文字を変な風に解釈していたようです。

というわけで、PythonでUTF-8扱うときはBOMは消そうと思いました。

2011年7月24日日曜日

ドリキャスの怪しげな商品購入してみた

社会人になって多少懐にも余裕が出たので、ネタ商品を購入してみた。

今回買ったのはこれです。

私が購入した時はAmazonでは品切れだったので、Yahoo!ショッピングを利用しました。
Dreamcast SDL/TF(microSD) / ドリームキャスト専用 バックアップツール
Dreamcast SDL/TF(microSD) / ドリームキャスト専用 バックアップツール


今現在(2011年7月)は4千円程度が相場でしょうか。
以前はオークションで8千円以上はしたドリームキャストのブロードバンドアダプターも、この方法ができてから値下がり傾向にあるっぽい?です。

商品説明に『※玄人専用』って書いてあるのがなんかアレゲです。

少し知識があればこちらのサイトを参考に自作できるようなもので、結構安く仕上がるものらしいです。
でも道具も知識もないのでとりあえず買ってみることにした。

使うもの
DC本体(MIL-CD対応版)
上の商品
microSD
CD

手順
dcsdripを http://f17.aaa.livedoor.jp/~takotako/dcserial_ft232bm.php からDL
↓
CDに焼く
参考: http://ayasuke.exblog.jp/14771039/
私も上のサイトと同じようにDir2bootを使用しましたが、DiscJugglerがなかったので代わりにImgBurnを使用しました。dllファイルがないと言われたのでそこは適当にやっつけた。
ImgBurnでよくわからないアラートが出たけど無視しても問題なかった。
↓
CDをDCにセットして吸出し
参考: http://ayasuke.exblog.jp/14773019/
ちなみに同じファイル名があった場合上書きされる仕様なので、2枚分のデータを1つのSDにやりたいときは名前変えるとよろし。
実機ではプレイできても、少しでも傷がついているものはリードエラーになるものが多かったです。
↓
nulldcをDL
http://code.google.com/p/nulldc/
上のページにあるように、Visual C++ 2010 RuntimesとDirectX 9c Runtimesが必要。
データフォルダにflashとbiosを名前を変えて置けば準備完了。

nulldcはShiftがスタートで、xcvbがABCD、asがLRとかでした。キーボードよりも360コントローラーでやるとやりやすそうですね。
しかし、nulldcの開発自体は去年から止まっているっぽい?
寄付も募集していたようですけど、今はどうなっているのかよくわらかないです。
あと、日本語フォルダ読めないようなので注意。

nulldcの完成度はそこそこですが、これ以上のアップデートもなさそうだし、互換が完璧というわけでもないので、やはりVGAボックスと実機でプレイというのが今のとこ一番かなぁ。

2011年4月26日火曜日

アイマス2 ファーストレビュー

最近プログラミングしてなくてネタがないのでゲームレビューする。

『アイドルマスター2』
まだクリアしてないので、ファーストレビューです。セカンド以降はあるか微妙ですが。


過去シリーズは、360版、L4U、SP(ミッシングムーン)、DSってプレイしてます。つまりAC版以外だいたいやってる。

アイマス待望の続編ってことで期待されてた作品ですが、発売前からネットで騒ぎ(?)を起こして、悪い意味で話題にもなった作品です。

プレイ時間は今のところ5時間程度。中盤なのかな?
あくまで今の時点でのレビューです。

・良い点
グラフィック
新曲
新キャラ(響と貴音)
S4U

良い点は上で挙げた通り。
S4Uはこれだけで楽しめるし自分は好きです。

・良くない点
曲の数
序盤
テンポ
ステージ

曲の数は特別少ないわけではありませんが、前作であった曲がないというのはさみしい。
容量的に厳しいのかもしれませんが、あったほうがよかったなーって思います。
また、今回はトリオを組むのが基本なのですが、序盤はメンバー内の仲が悪かったりします。
正直アイドル同士の仲が悪いのを見たくないw
やよいが、「今日も仲良し2人組で、、あっ! すみません! 3人組 で・・・」なんて言うとか耐えられないぉ(´;ω;`)

あと相変わらずテンポが悪いです。
もうこれはシリーズ通してそう感じてるんで半分諦めてますが・・・
「突風アイドル・・・」とかいらねーから。
ついでに言うとレッスンもいらないと個人的に思う。中途半端なアクションとかいらない、シミュレーションでいい。

最後にステージについてですが、残念だと思うことが2つ。
1つは歌うパートが選べないということ。ユニットのポジションで決まってるっぽい?
もう1つはカメラ。これも選べなくなっているんですが、自分はむしろそれよりもDSにあった視点がなくなってるのが残念です。
DSでは、アイドル後方から観客席を移すカメラアングルがあったんですよね。
こんなの
自分はこれが気に入っていて、DSで一番良かったものだと思ってるくらい。

まだやりこんでないので、間違ってたらごめんなさい。
悪い点ばかり長く書いてますが、つまらないってわけじゃないです。
中盤以降はやることもわかってきて、アイドルもギスギスしてないので面白いです。


というか2でサイネリアが1位とかどういうことなの・・・

DSってゲーム性がなくて、あまり評価されてない面もありますが、自分は好きです。
変態的な(主に涼ちん)シナリオが目立ちますが、システムも頑張ってたんだなーと。まあグラフィックと音質はDSなんであれなんですが。

2011年4月23日土曜日

FlexのBase64Decoder

DC startup generatorのソースコードを少し修正した。
んで、mx.utils.Base64Decoderを使うときにちょっと躓いたのでメモ。

修正個所は、zlib圧縮をやめたのと、
修正前はライブラリを使ってBase64を扱っていたのですが、どうやらFlex4には最初から
mx.utils.Base64Decoder
なるものがあるらしいので使ってみることにした。

*自分はFlash Builder 4で開発してます。

まず、普通にAS3プロジェクトを作ってもBase64Decoderをインポートできない。
「framework.swc」というswcファイルが必要になります。
Base64Encoder/Base64Decoderを使う
これはSDKがあるディレクトリの、[frameworks/libs]の中にあります。

しかしまだエラーが残るので厄介。
Base64Decoder 使おうとしたら「リソースバンドルutilsを解決できません」とか言い出したのでやっつけた
なぜかダミーのフォルダを作らないといけないようです・・・

あとは問題なく使えます。
設定さえ終われば便利です。

以下ソースコード。
package
{
 import flash.display.Sprite;
 import flash.display.StageScaleMode;
 import flash.events.Event;
 import flash.filters.BlurFilter;
 import flash.net.URLRequest;
 import flash.net.navigateToURL;
 import flash.text.TextField;
 import flash.text.TextFormat;
 import flash.utils.ByteArray;
 
 import mx.utils.Base64Decoder;
 
 import org.libspark.betweenas3.BetweenAS3;
 import org.libspark.betweenas3.easing.Cubic;
 import org.libspark.betweenas3.easing.Quad;
 import org.libspark.betweenas3.tweens.ITween;
 
 public class DC extends Sprite
 {
  private function Base64toByteArray(data:String):ByteArray
  {
   var byteArray:ByteArray;
   var base64Decoder:Base64Decoder = new Base64Decoder();
   base64Decoder.decode(data);
   try {
    byteArray = base64Decoder.toByteArray();
    byteArray.position = 0;
   } catch (e:Error) {
    return null;
   }
   return byteArray;
  }
  
  private const R:uint = 60;
  private const W:Number = stage.stageWidth / 2;
  private const H:Number = stage.stageHeight / 2;
  private var _letter:Array = [];
  private var _ball:Ball;
  private var _temp:TextField = new TextField();
  private var _bounceTiem:Number = 0.36;
  
  public function DC()
  {
   stage.scaleMode = StageScaleMode.SHOW_ALL;
   stage.frameRate = 60;
   
   var param:Object = loaderInfo.parameters;
   var word:String = param["w"];
   if(word == null)
    word = "Dreamcast";
   else {
    var ba:ByteArray = new ByteArray();
    ba = Base64toByteArray(word);
    if(!ba) {
     navigateToURL(new URLRequest("/dc/"));
    } else {
     word = ba.readUTFBytes(ba.length);
    }
   }
   _temp.text = word;
   var format:TextFormat = new TextFormat(null, R);
   _temp.setTextFormat(format);
   
   for(var i:int = 0; i < _temp.text.length; i++)
   {
    var tf:TextField = new TextField();
    tf.text = _temp.text.charAt(i);
    tf.selectable = false;
    tf.setTextFormat(format);
    
    if(i)
     tf.x = _letter[i-1].x + _letter[i-1].textWidth;
    else
     tf.x = W - _temp.textWidth / 2;
    tf.y = H + R/2;
    _letter.push(tf);
   }
   
   _ball = new Ball();
   _ball.filters = [new BlurFilter()];
   _ball.x = _letter[0].x - R*2.2;
   _ball.y = _letter[0].y - R*4;
   addChild(_ball);
   
   var tween:ITween = BetweenAS3.bezierTo(_ball, {x: _letter[0].x + _letter[0].textWidth, y: _letter[0].y + 35}, {x: _letter[0].x + R, y: _letter[0].y - R}, 2, Cubic.easeIn);
   tween.onComplete = bounce;
   tween.onCompleteParams = [1];
   tween.play();
  }
  
  private function bounce(n:uint):void
  {
   character(n-1);
   var tween:ITween = BetweenAS3.bezierTo(_ball, {x: _letter[n].x + _letter[n].textWidth, y: _ball.y}, {x: _letter[n].x + _letter[n].textWidth / 4, y: _letter[n].y - 10}, _bounceTiem);
   if(_bounceTiem >= 0.22) {
    _bounceTiem -= 0.02;
   }
   if(n < _temp.text.length - 1)
    tween.onComplete = function():void
    {
     bounce(n+1)
    };
   else
    tween.onComplete = ぴょーん;
   tween.play();
  }
  
  private function character(index:uint):void
  {
   addChild(_letter[index]);
   BetweenAS3.tween(_letter[index], {y: _letter[index].y, alpha: 1}, {y: _letter[index].y + 20, alpha: 0}).play();
  }
  
  private function ぴょーん():void
  {
   character(_temp.text.length - 1);
   
   var tween:ITween = BetweenAS3.bezierTo(_ball, {x: W, y: _letter[0].y - R*0.8}, {x: (W + _ball.x) / 2, y: _letter[0].y - R*5}, 1.2, Quad.easeOut);
   tween.play();
   tween.onComplete = ぐるぐる;
  }
  
  private function ぐるぐる():void
  {
   var sp:Sprite = new Sprite();
   sp.filters = [new BlurFilter()];
   addChild(sp);
   sp.graphics.lineStyle(6, _ball.color);
   sp.graphics.moveTo(_ball.x, _ball.y);
   
   addEventListener(Event.ENTER_FRAME, onEnterFrame(sp));
   
   var tweenArray:Array = [];
   for(var i:int = 0; i < 900; i++)
   {
    var toX:Number = _ball.x - R * i / 720 * Math.cos(i * Math.PI / 180);
    var toY:Number = _ball.y - R * i / 720 * Math.sin(i * Math.PI / 180);
    tweenArray.push(BetweenAS3.to(_ball, {x: toX, y: toY}, 0.002));
   }
   var tween:ITween = BetweenAS3.serialTweens(tweenArray);
   tween.onComplete = function():void
   {
    removeEventListener(Event.ENTER_FRAME, onEnterFrame);
    sp.graphics.lineTo(_ball.x, _ball.y);
    removeChild(_ball);
   };
   tween.play();
  }
  
  private function onEnterFrame(sp:Sprite):Function
  {
   return function(e:Event):void
   {
    sp.graphics.lineTo(_ball.x, _ball.y);
   };
  }
 }
}

import flash.display.Sprite;

class Ball extends Sprite
{
 private var _color:uint;
 
 public function get color():uint
 {
  return _color;
 }
 
 public function Ball(j:Boolean = true) {
  if(j)
  {
   graphics.beginFill(0xea5504);
   _color = 0xea5504;
  } else {
   graphics.beginFill(0x0000FF);
   _color = 0x0000FF;
  }
  graphics.drawCircle(0, 0, 4);
  graphics.endFill();
 }
}

2011年4月18日月曜日

BottomCoder SRM398 DIV2

下書きのままだったので公開する。

昔過ぎてもう問題覚えてない。
たしかこれはどっちも解けたはず。

import java.util.Arrays;

public class MinDifference {
 public double EPS = 1e-6;
 
 public int closestElements(int A0, int X, int Y, int M, int n) {
  int[] a = new int[n];
  a[0] = A0;
  for(int i = 1; i < n; i++) {
   a[i] = (a[i - 1] * X + Y) % M;
  }
  Arrays.sort(a);
  int res = Integer.MAX_VALUE;
  for(int i = a.length - 1; i > 0; i--) {
   res = Math.min(res, a[i] - a[i - 1]);
  }
  return res;
 }
 
 public void tr(Object... o) { System.out.println(o.length > 1 || o[0].getClass().isArray() ? Arrays.deepToString(o) : o[0]); }
}


激しい。
こんなことしなくても解ける・・・
import java.util.Arrays;
import java.util.LinkedList;

public class CountExpressions {
 public double EPS = 1e-6;
 
 private LinkedList list = new LinkedList();
 private int res = 0;
 private int ans;
 
 public int calcExpressions(int x, int y, int val) {
  ans = val;
  
  list.add(x);
  list.add(x);
  list.add(y);
  list.add(y);
  dfs("+", 0, list.poll(), 0);
  
  list = new LinkedList();
  list.add(x);
  list.add(y);
  list.add(x);
  list.add(y);
  dfs("+", 0, list.poll(), 0);

  list = new LinkedList();
  list.add(x);
  list.add(y);
  list.add(y);
  list.add(x);
  dfs("+", 0, list.poll(), 0);

  list = new LinkedList();
  list.add(y);
  list.add(y);
  list.add(x);
  list.add(x);
  dfs("+", 0, list.poll(), 0);

  list = new LinkedList();
  list.add(y);
  list.add(x);
  list.add(x);
  list.add(y);
  dfs("+", 0, list.poll(), 0);

  list = new LinkedList();
  list.add(y);
  list.add(x);
  list.add(y);
  list.add(x);
  dfs("+", 0, list.poll(), 0);
  
  tr(res);
  return res;
 }
 
 private void dfs(String s, long val, int v, int n) {
  if(s.equals("+")) {
   val += (long)v;
  } else if(s.equals("-")) {
   val -= (long)v;
  } else {
   val *= (long)v;
  }
  
  if(n > 2) {
   if(val == ans) {
    res++;
   }
   list.add(0, v);
   return;
  }

  dfs("+", val, list.poll(), n + 1);
  dfs("-", val, list.poll(), n + 1);
  dfs("*", val, list.poll(), n + 1);
  list.add(0, v);
 }
 
 public void tr(Object... o) { System.out.println(o.length > 1 || o[0].getClass().isArray() ? Arrays.deepToString(o) : o[0]); }
}

2011年3月8日火曜日

零の軌跡クリアした

先日やっと『零の軌跡』をクリアしました。



それなりに面白かったです。いい意味でも悪い意味でもシステム面で過去作からの大きな変化はない。
個人的には、戦闘システムをちょっと変えて欲しい。

プレイ時間はセーブデータを見ると、79時間39分(ハード)。RPGとしてはたぶん一般的な長さだと思う。
ただ今の自分にとってはちょっと長い・・・買ったのは去年の10月だからクリアまで半年ぐらいかかってる。
英雄伝説の空の軌跡シリーズは全部PSPでプレイしてますが、各作品のプレイ時間は
FC 53時間22分(ノーマル)
SC 90時間36分(ノーマル)
3rd 58時間43分(ノーマル)
となっていて過去作と比べても長めなのかも。

今回は初回プレイからハードにした。
戦闘は銀戦が一番苦労した。ラスボスより面倒だよね、あれ。

2010年12月25日土曜日

BottomCoder SRM403 DIV2

これからは本番はTopCoderってタイトルで、練習はBottomCoderってタイトルにします。
最近TopCoderの記事が多いのでもっとほかの記事も書きたいです。

250
public class TheLargestLuckyNumber {
 public int find(int n) {
  outer: for(int i = n; i >= 4; i--) {
   for(int j = 0; j < Integer.toString(i).length(); j++) {
    if(Integer.toString(i).charAt(j) != '7' && Integer.toString(i).charAt(j) != '4') {
     continue outer;
    }
   }
   return i;
  }
  
  return 0;
 }
}
早く解けた。2重ループだとラベル便利。

500
public class TheLuckyNumbers {
 public int count(int a, int b) {
  int count = 0;
  String sa = Integer.toString(a);
  String sb = Integer.toString(b);
  int len = sb.length();
  for(int j = 1; j <= len; j++) {
   for(int mask = 0; mask < (1 << j); mask++) {
    String str = "";
    for(int i = 0; i < j; i++) {
     if((mask & (1 << i)) > 0) {
      str += "7";
     } else {
      str += "4";
     }
    }
    if((str.length() > sa.length() || (str.length() == sa.length() && str.compareTo(sa) >= 0)) && (str.length() < sb.length() || str.compareTo(sb) <= 0)) {
     count++;
    }
   }
  }
  return count;
 }
}
250のように全探索すると、2秒以内に終わらないので計算量を減らす必要がある問題。
いろいろな解き方でアプローチしてて時間内に終わらなかった。
自力で解くことはできたけど、これぐらいの問題を解けないと緑安定はしないし、解けるようになりたい。
roseとlilyのときにも使ったビットを使った方法で解いてます。この問題は解き方いくつかありそう。

2010年12月14日火曜日

TopCoder練習 SRM424 DIV2

250問題文
public class MagicSpell {
 public String fixTheSpell(String spell) {
  String magic = new String();
  for(int i = 0; i < spell.length(); i++) {
   if(spell.charAt(i) == 'A' || spell.charAt(i) == 'Z') {
    magic += spell.charAt(i);
   }
  }
  String magicSpell = new String();
  int count = 0;
  for(int i = 0; i < spell.length(); i++) {
   if(spell.charAt(i) == 'A' || spell.charAt(i) == 'Z') {
    magicSpell += magic.charAt(magic.length()-1-count);
    count++;
   } else {
    magicSpell += spell.charAt(i);
   }
  }
  return magicSpell;
 }
}
問題文が読めれば解ける。コードは汚い。

500

import java.util.ArrayList;

public class ProductOfDigits {
 public int smallestNumber(int N) {
  if(N < 10) return 1;
  
  ArrayList<Integer> list = new ArrayList<Integer>();
  for(int i = 9; i > 1; i--) {
   if(N % i == 0) {
    list.add(i);
   }
  }
  if(list.size() < 1) return -1;
  
  int index = 0;
  int a = 0;
  while(N > 1) {
   if(N % list.get(index) == 0) {
    a++;
    N /= list.get(index);
   } else {
    if(index < list.size() -1) {
     index++;
    } else {
     return -1;
    }
   }
  }
  return a;
 }
}
問題文の意味よく分からなくて1文字もかけなかった。

2010年12月13日月曜日

TopCoder練習 SRM 423 DIV2

250問題文
public class TheSimpleGame {
 public int count(int n, int[] x, int[] y) {
  int a = (n+1)/2;
  int b = 0;
  for(int i = 0; i < x.length; i++) {
   b += (x[i] <= a) ? x[i] - 1 : n - x[i];
   b += (y[i] <= a) ? y[i] - 1 : n - y[i];
  }
  return b;
 }
}
簡単。

600
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class TheTower {
 public int[] count(int[] x, int[] y) {
  int[] a = new int[x.length];
  a[0] = 0;
  if(x.length == 1) {
   return a;
  }
  ArrayList<Integer> xList = new ArrayList<Integer>();
  ArrayList<Integer> yList = new ArrayList<Integer>();
  for(int i = 0; i < x.length; i++) {
   boolean flag1 = false;
   boolean flag2 = false;
   for(int j = 0; j < xList.size(); j++) {
    if(x[i] == xList.get(j)) {
     flag1 = true;
     break;
    }
   }
   if(!flag1) {
    xList.add(x[i]);
   }
   for(int j = 0; j < yList.size(); j++) {
    if(y[i] == yList.get(j)) {
     flag2 = true;
     break;
    }
   }
   if(!flag2) {
    yList.add(y[i]);
   }
  }
  for(int i = 1; i < x.length; i++) {
   ArrayList<Integer> list = new ArrayList<Integer>();
   for(int j = 0; j < xList.size(); j++) {
    for(int k = 0; k < yList.size(); k++) {
     list.add(checkCell(xList.get(j), yList.get(k), i+1, x, y));
    }
   }
   Collections.sort(list);
   a[i] = list.get(0);
  }
  return a;
 }
 
 private int checkCell(int x, int y, int n, int[] arrayX, int[] arrayY) {
  int[] array = new int[arrayX.length];
  for(int i = 0; i < array.length; i++) {
   array[i] = (int)Math.abs((double)(x - arrayX[i])) + (int)Math.abs((double)(y - arrayY[i]));
  }
  Arrays.sort(array);
  int move = 0;
  for(int i = 0; i < n; i++) {
   move += array[i];
  }
  return move;
 }
}
間違えたので後で修正した解答。
問題文は読めても結構難しい問題だと思う。これが600か。
おそらく、移動先のx,yはそれぞれのメジアン(中央値)になる。(ただしチェッカーが偶数のときは中央2つの平均ではなく、その2つのどちらかになる
そう考えると、x座標のリストと、y座標のリストの組み合わせ分だけ考慮すればいいので、その中で一番移動数が少ないものを選ぶ。

TopCoder SRM490 DIV2

2回目。落ち込む。チャレンジやってみたくてやったけど失敗した。
250 ○
500 未提出
1000 未提出
Rate 955 -> 789

TopCoder SRM489 DIV2

初めてのTopCoder本番ってわけだったんですが、結果は何とも言えないものにw
250 チャレンジ
500 未提出
1000 未提出

1問も解けなく、点数はもちろん0なわけですが、レートは955というそれなりに高いという結果に。いきなり緑。
どうやら250の正解率が低かったみたい。

2010年12月9日木曜日

最近買ったの

先日ホリパッドEX2ターボっていうXbox360のコントローラーを購入しました。(黒い方



感想としては、悪くはないなーって感じです。
もともと自分はXbox360の純正コントローラーが使いやすいって思っていた人なんで、アナログスティックとかは純正のほうが使いやすいと感じてます。しかし、純正のは方向キーがクソなので、それはこちらのほうがいいです。不満はありますが。

そもそもなぜこのコントローラーを買ったかというと、「コントローラーで格闘ゲームやりたい」「PC用コントローラーに前面6ボタンのものが欲しい」っていうのが大きな理由。
トーナメントエディション ファイトパッド for Xbox 360とどちらを購入するか少し迷いましたが、安い方を購入しました。
連射機能は特別欲しかったわけではありませんが、あったらあったで便利です。一部の実績の解除も楽になります。

このコントローラーでSSFをちまちまプレイしていくです。