-- / --
--
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

10 / 21
Mon

ここでオートタイルの処理を行った画像を上げましたが、 オートタイルの処理のコードはこんな感じになってます。

def autotileNumber(x: Int, y: Int): Array[Int] = {
  val direction: Array[Array[Int]] = Array(Array(x - 1, y - 1), Array(x + 1, y - 1), Array(x - 1, y + 1), Array(x + 1, y + 1),
                                           Array(x - 1, y + 1), Array(x + 1, y + 1))
  val number: Array[Array[Int]] = Array(Array(5, 11, 4, 1, 0), Array(5, 15, 6, 1, 2), Array(5, 3, 4, 9, 8),
                                        Array(5, 7, 6, 9, 10), Array(16, 16, 16, 13, 12), Array(16, 16, 16, 13, 14))
  var resultArray: ArrayBuffer[Int] = ArrayBuffer()
  for (i <- 0 to 5) {
    val fx: Int = direction(i)(0)
    val fy: Int = direction(i)(1)
    var result: Int = 16
    (isAutotile(fx, fy), isAutotile(fx, y), isAutotile(x, fy)) match {
    case (true, true, true) =>
      result = number(i)(0)
    case (false, true, true) =>
      result = number(i)(1)
    case (_, false, true) =>
      result = number(i)(2)
    case (_, true, false) =>
      result = number(i)(3)
    case _ =>
      result = number(i)(4)
    }
    resultArray += result
  }
  return resultArray.toArray
}

返される値は、 左上の天井, 右上の天井, 左下の天井, 右下の天井, 左の壁, 右の壁のオートタイル番号を順に格納した配列です。 オートタイル番号というのは、 この記事の 2 枚目の画像で表示されている番号です。

同じ処理を 2 年くらい前に Ruby で書いたときよりコード量がかなり減っています。 たぶん半分くらい。 まあ、 2 年前のコードは無駄が多かったというのもあるんですが、 Scala のパターンマッチのおかげで、 かなり綺麗に短くコードが書けました。 ・・・その代わりに、 簡略化しすぎて direction とか number とかの変数が何を表してるのか分かりにくくなりましたけどね。

あ、 そうそう、 Scala には型推論という非常に強力な機能がありますが、 私は全然利用してません。 全ての変数に型を明示してます。 何か書かないと落ち着かないんですよ・・・。


スポンサーサイト
comment ×0
10 / 06
Sun

また Scala です。 Java にはない新しいことがたくさんあるので、 調べてると飽きません。 そろそろドット絵も描かないとですね・・・。

RPG のマップを管理するクラスを作ってました。 タイルデータは 2 次元配列で管理しています。 配列の長さとか内容とかが変化する可能性があるので、 scala.collection.mutable パッケージにある ArrayBuffer クラスを使っています。 まず、 クラス内でこんな感じで初期化されます。

private var map: ArrayBuffer[ArrayBuffer[Int]] = new ArrayBuffer[ArrayBuffer[Int]]

で、 コンストラクタで外部ファイルを読み込んで、 タイルデータを格納していきます。

val source: Source = Source.fromFile("data/data/map/" + number + "-" + floor + ".txt")
val regexData: Regex = "([\\d\\s,]+)".r
try {
  for (line <- source.getLines) {
    line match {
    case regexData(data) =>
      map += ",".r.split(line).to[ArrayBuffer].map((string: String) => string.trim.toInt)
    case _ =>
    }
  }
} finally {
  source.close
}

この段階ではわざわざ case ~ match 構文を使う必要はないんですが、 今後ファイルから読み込むデータが増えたときに簡単に追加できるよう、 この構文を使って書いておきました。 あ、 number と floor は、 それぞれマップ番号と (ダンジョンなどの) 階層が格納してある変数です。

map += ~ の部分が複雑になっちゃったんですよね。 まず、 split メソッドでコンマごとに区切ったリストを生成し、 それを to メソッドで ArrayBuffer オブジェクトに変換した後、 map メソッドで要素を Int オブジェクトに変換してます。 (string: String) => string.trim.toInt_.trim.toInt と書けばそれで十分ですが、 アンダースコアはあまり使いたくないのです。

そうそう、 最初は string.trim.toIntstring.toInt だけにしていたので、エラー吐かれて困ってました。 どうやら先頭に空白があると変換できないみたいですね。 融通が利かないなぁ・・・。 Ruby では " 12".to_i はちゃんと機能しましたよ?

あ、 あと case _ => も書き忘れてて困ってました。 case ~ match 構文はパターンが網羅されてないとだめっぽいです。


comment ×0
10 / 06
Sun

先日紹介した replaceAll メソッド、 「便利じゃないですか」 とか言っていてましたが、 やっぱり便利で需要があるようで、 Scala の scala.util.matching.Regex クラスに replaceAllIn メソッドの形で、 すでに実装されていました。 いや、 私の苦労は何だったんだ・・・。 もともと Java 使ってたので、 Scala の正規表現についてはよく知らなかったんですよね。

ということで、 正規表現について調べてみました。 まず、 文字列から正規表現にマッチした部分を取り出す処理です。 あ、 事前に scala.util.matching.Regex と scala.util.matching.Regex.Match はインポートしておく必要があります。

val string: String = "Many kinds of wild animals have been disappearing"
val regex: Regex = "\\w+".r
for (matched <- regex.findAllIn(string).matchData) {
  println("\"" + matched.matched + "\" at " + matched.start)
}

パターンマッチとかいろいろできるみたいですが、 Scala にまだ慣れてないせいでコードがいまいちよく分からないんですよね。 分かりしだい、 ここにメモしておきます。


comment ×0
10 / 05
Sat

Java の String クラスには replaceAll というメソッドがあって、 これが正規表現を使えるので便利なんです。 例えば、 こんな感じです (Scala で書いてあります)。

val string = "1234abc56de789fgh0"
val result = string.replaceAll("\\d+", "N")
println(result)

実行すると NabcNdeNfghN が表示されます。

さて、 なかなか便利なんですが、 1 つ欠点があります。 例えば、 正規表現にマッチした部分が 100 以上だったら M に置換し、 そうでなかったら N に置換したい場合はどうすれば良いでしょうか。 replaceAll メソッドは、 置換先は固定の文字列なので、 こういったことはできません。 そこで、 Scala の特長の 1 つである引数に関数をとれることを利用して、 replaceAll メソッドをオーバーロードしてみます。

def replaceAll(regex: String, replacer: String => String): String = {
  val matcher: Matcher = Pattern.compile(regex).matcher(string)
  val buffer: StringBuffer = new StringBuffer
  while (matcher.find) {
    matcher.appendReplacement(buffer, replacer(matcher.group))
  }
  matcher.appendTail(buffer)
  return buffer.toString
  }
}

このメソッドを使うと、 先に挙げた例も実現できます。

val string = "1234abc56de789fgh0"
val result = string.replaceAll("\\d+", (matched: String) => {
  if (matched.toInt >= 100) {
    "M"
  } else {
    "N"
  }
})
println(result)

実行すると MabcNdeMfghN が表示され、 正しく置換されていることが分かります。

かなり便利じゃないですか? しかも、 Ruby と同じように書けるところが好きです。 ちなみに、 Ruby で同じことをするとこんな感じになります。

string = "1234abc56de789fgh0"
result = string.gsub(/\d+/) do |matched|
  if (matched.to_i >= 100)
    "M"
  else
    "N"
  end
end
puts(result)

comment ×0
10 / 04
Fri

Scala っていうプログラミング言語を見つけました。 これがあまりに素晴らしかったんで、 今後の開発環境は Java から Scala に変更することにしました。

Java にはプリミティブ型というものがあって、 正直私にとってはこれは受け入れがたかったんです。 慣れ親しんだ Ruby のような完全なオブジェクト指向言語だと、 オブジェクトでないものは存在しないからです。 でもかといって、 Ruby は Java と比べて 10 倍遅いですし、 ゲーム用のライブラリもそんなにない。 そこで、 Scala を発見したわけです。

まず Scala は Java プラットフォーム上で動くので、 速度は Java とほぼ同じくらい。 そのおかげで Java との連携も簡単で、 普通に JFrame とか JPanel とかが使える。 加えて完全なオブジェクト指向。 コードの見た目も気に入っていた Ruby に似ていて、 私が受け入れやすい。 他にも様々な理由があるのですが、 もう Scala 一択です。

Java を受け継ぎながら、 私が Java の嫌いな点を消し去った感じです。 素晴らしいです。


comment ×0
back-to-top
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。