パイクラおじさんの日記

MinecraftでPythonを勉強するおじさんの日記です。

マイクラの高度に関する情報

テレポートカプセル(以下、tpcと記す)でいろいろ調査してみると、地下の深さがクリエイティブ・モードとサバイバル・モードで違う。

クリエイティブ・モードでは地下は-4までで、-5には設置もできない。 サバイバル・モードでは地下は-60くらいまではあるが、tpcで-65に行くと底が抜けて(底部分が設置できずに)奈落へ落ちてしまう。

上空もクリエイティブ・モードだと251まではブロックを設置できるけど、サバイバル・モードではtpcで190に行くと上半分が設置できない状態になる。

で、今日、この次のページを見つけてわかった。

高度 - Minecraft Wiki

マイクラでは、全体で0〜255まで設置可能で、pythonによる指定できる座標の原点がクリエイティブ・モードでは高度4の位置にあり-4〜251の256の範囲になり、サバイバル・モードでは高度62(?)の位置にあるため、-62〜192くらいのやはり256の範囲で設置可能になるということらしい。ただし、サバイバル・モードでのpython原点が固定かどうかは不明。

テレポートカプセル

サバイバルモードの制限を調査するのに、サバイバルモードでは飛行モードが無いので高い所に行くのにテレポート・コマンド(実際にはsetPos関数)で移動する前に足場を作らないといけないし、地下も足場を作って、自分の入る空間を作って、さらに松明を設置してからテレポートする。 これがめんどくさくて、指定した位置にガラス製のカプセルを作って、その中にsetPosで移動するスクリプトを書いた。

スクリプトtpc.py)

# coding: utf-8

import mcpi.minecraft as minecraft
import mcpi.block as block
import sys

if len(sys.argv) < 4:
    sys.exit("Usage: tcp.py x y z")

x = int(sys.argv[1])
y = int(sys.argv[2])
z = int(sys.argv[3])

mc = minecraft.Minecraft()

mc.setBlocks(x-1, y, z-1, x+1, y+3, z+2, block.GLASS)
mc.setBlocks(x, y+1, z, x, y+2, z, block.AIR)
mc.setBlock(x, y+1, z+1, block.GLASS)
mc.setBlock(x, y+2, z+1, block.TORCH)
mc.player.setPos(x+0.5, y+1.0, z+0.5)

実行すると、こんな感じ。

f:id:pycra:20171015102508p:plain

f:id:pycra:20171015102514p:plain

トーテムポール的なモノ

方位がわかるようなトーテムポール的なオブジェを剣+右クリックで設置できるといいなぁ〜と、まずはどんな形がいいかと作ってみた。

f:id:pycra:20171014090436p:plain

(もうちょっと上にしたい。)

いろいろ調整して、こんな感じになった。

f:id:pycra:20171014144041p:plain

夜になってもわかるように松明を付けた。

f:id:pycra:20171014144513p:plain

スクリプト

wood.pyと同じく、pycraパッケージにdirobj.pyモジュールとして作った。

pycra/dirobj.py

# coding: utf-8

import mcpi.minecraft as minecraft
import mcpi.block as block

def dirobj(mc, x, y, z):
    gy = y+12
    # Green Arrow
    mc.setBlocks(x, gy, z, x, gy+11, z, block.HARDENED_CLAY_STAINED_GREEN)
    mc.setBlock(x-1, gy+10, z, block.HARDENED_CLAY_STAINED_GREEN)
    mc.setBlock(x+1, gy+10, z, block.HARDENED_CLAY_STAINED_GREEN)
    mc.setBlock(x, gy+10, z-1, block.HARDENED_CLAY_STAINED_GREEN)
    mc.setBlock(x, gy+10, z+1, block.HARDENED_CLAY_STAINED_GREEN)
    mc.setBlock(x-2, gy+9, z, block.HARDENED_CLAY_STAINED_GREEN)
    mc.setBlock(x+2, gy+9, z, block.HARDENED_CLAY_STAINED_GREEN)
    mc.setBlock(x, gy+9, z-2, block.HARDENED_CLAY_STAINED_GREEN)
    mc.setBlock(x, gy+9, z+2, block.HARDENED_CLAY_STAINED_GREEN)
    cy = gy+5
    # Red Arrow
    mc.setBlocks(x-5, cy, z, x+6, cy, z, block.HARDENED_CLAY_STAINED_RED)
    mc.setBlock(x+5, cy+1, z, block.HARDENED_CLAY_STAINED_RED)
    mc.setBlock(x+5, cy-1, z, block.HARDENED_CLAY_STAINED_RED)
    mc.setBlock(x+4, cy+2, z, block.HARDENED_CLAY_STAINED_RED)
    mc.setBlock(x+4, cy-2, z, block.HARDENED_CLAY_STAINED_RED)
    # Blue Arrow
    mc.setBlocks(x, cy, z-5, x, cy, z+6, block.HARDENED_CLAY_STAINED_BLUE)
    mc.setBlock(x, cy+1, z+5, block.HARDENED_CLAY_STAINED_BLUE)
    mc.setBlock(x, cy-1, z+5, block.HARDENED_CLAY_STAINED_BLUE)
    mc.setBlock(x, cy+2, z+4, block.HARDENED_CLAY_STAINED_BLUE)
    mc.setBlock(x, cy-2, z+4, block.HARDENED_CLAY_STAINED_BLUE)
    # Pillar
    mc.setBlocks(x, y, z, x, gy-1, z, block.HARDENED_CLAY_STAINED_WHITE)
    # Torch
    mc.setBlock(x, gy+12, z, block.TORCH)
    mc.setBlock(x-3, gy+9, z, block.TORCH.id, 2)
    mc.setBlock(x+3, gy+9, z, block.TORCH.id, 1)
    mc.setBlock(x, gy+9, z-3, block.TORCH.id, 4)
    mc.setBlock(x, gy+9, z+3, block.TORCH.id, 3)
    mc.setBlock(x+7, cy, z, block.TORCH.id, 1)
    mc.setBlock(x+6, cy, z+1, block.TORCH.id, 3)
    mc.setBlock(x+6, cy, z-1, block.TORCH.id, 4)
    mc.setBlock(x, cy, z+7, block.TORCH.id, 3)
    mc.setBlock(x+1, cy, z+6, block.TORCH.id, 1)
    mc.setBlock(x-1, cy, z+6, block.TORCH.id, 2)

木の時と同じ感じに。

mkdirobj.py

# coding: utf-8

import mcpi.minecraft as minecraft
import mcpi.block as block
import pycra.dirobj as dirobj

mc = minecraft.Minecraft()
mc.events.clearAll()

while True:
    blockHits = mc.events.pollBlockHits()
    if blockHits:
        blockHit = blockHits[0]
        if blockHit.face == 1:
            x = blockHit.pos.x
            y = blockHit.pos.y
            z = blockHit.pos.z
            blockId = mc.getBlock(x, y, z)
            while blockId != block.AIR:
                y = y + 1
                blockId = mc.getBlock(x, y, z)
            dirobj.dirobj(mc, x, y, z)

軌道の中心線(1)

基盤地図情報の基本項目をダウンロードしてくると、基本項目のいろんなデータが一緒に入ったZIPファイルになっている。

軌道の中心線のデータは緯度経度で指定されているので、1次メッシュ番号、2次メッシュ番号から2次メッシュ上のインデックスへ変換して軌道用の配列へセットするまでを実装してみた。

f:id:pycra:20171014080647p:plain

ここに貼った段階で点が潰れてしまっているけど、まだ点をプロットしただけなのでこれから点を繋いでいく処理を入れないといけない。

スクリプト(railcl.py)

# coding: utf-8

import xml.etree.ElementTree as ET
import numpy as np
import matplotlib.pyplot as plot
import pycra.npydata as nd
import sys

NPY_DATA = "npydata-5340-22.npy"
XML_RAILCL = "FG-GML-534022-ALL-20170401/FG-GML-534022-RailCL-20170401-0001.xml"
RCL_DATA = "npyrcl-5340-22.npy"

class World:
    """ class for World coordinate """
    def __init__(self, mesh1, mesh2, geo_array, rcl_array):
        self.mesh1 = mesh1
        self.mesh2 = mesh2
        self.geo_array = geo_array
        self.rcl_array = rcl_array
        self.origin1 = Position.convert_mesh1(mesh1)
        self.origin2 = Position.convert_mesh2(mesh1, mesh2)

    def convert_position(self, pos):
        offset = Position(pos.latitude - self.origin2.latitude, pos.longitude - self.origin2.longitude)
        index = Index(int(1499.0 * (1.0 - (offset.latitude / ((1.0/1.5)/8.0)))), int(2249.0 * offset.longitude / (1.0/8.0)))
        return index

class RailCL:
    """ class for Rail Center Line """
    def __init__(self, elem):
        e = elem.find("{http://fgd.gsi.go.jp/spec/2008/FGD_GMLSchema}vis")
        if e is None:
            raise Exception("{http://fgd.gsi.go.jp/spec/2008/FGD_GMLSchema}vis is not found")
        if e.text == "表示":
            self.vis = True
        else:
            self.vis = False
        e = elem.find("{http://fgd.gsi.go.jp/spec/2008/FGD_GMLSchema}loc/{http://www.opengis.net/gml/3.2}Curve/{http://www.opengis.net/gml/3.2}segments/{http://www.opengis.net/gml/3.2}LineStringSegment/{http://www.opengis.net/gml/3.2}posList")
        if e is None:
            raise Exception("{http://www.opengis.net/gml/3.2}posList is not found")
        self.array = text2array(e.text.strip())

class Position:
    """ class for Latitude, Longitude """
    @classmethod
    def convert_mesh1(self, mesh1):
        return Position(float(mesh1//100)/1.5, float(mesh1%100)+100.0)

    @classmethod
    def convert_mesh2(self, mesh1, mesh2):
        pos = Position(float(mesh1//100)/1.5, float(mesh1%100)+100.0)
        offset = Position((1.0/1.5)/8.0 * (mesh2//10), 1.0/8.0 * (mesh2%10))
        pos.add(offset)
        return pos

    def __init__(self, latitude, longitude):
        self.latitude = latitude
        self.longitude = longitude

    def add(self, offset):
        self.latitude += offset.latitude
        self.longitude += offset.longitude

    def __str__(self):
        return "({0:f}, {1:f})".format(self.latitude, self.longitude)

class Index:
    """ class for Array index """
    def __init__(self, y, x):
        self.y = y
        self.x = x

    def __str__(self):
        return "({0:d}, {1:d})".format(self.y, self.x)

def text2array(text):
    lines = text.split()
    array = np.empty((len(lines)), dtype=np.float)
    i = 0
    for l in lines:
        array[i] = float(l)
        i += 1
    return array.reshape((len(lines)//2, 2))

geo_array = np.load(nd.NPY_DIR+"/"+NPY_DATA)

tree = ET.parse(XML_RAILCL)
root = tree.getroot()

rails = []

for e in root.getiterator("{http://fgd.gsi.go.jp/spec/2008/FGD_GMLSchema}RailCL"):
    rc = RailCL(e)
    rails.append(rc)

world = World(5340, 22, geo_array, rails)
print("World.origin1="+str(world.origin1))
print("World.origin2="+str(world.origin2))
(y_max, x_max) = world.geo_array.shape
print("World.geo_array.shape=({0}, {1})".format(y_max, x_max))

array = np.zeros(world.geo_array.shape)
for r in rails:
    (y_max, x_max) = r.array.shape
    for y in range(y_max):
        pos = Position(r.array[y][0], r.array[y][1])
        index = world.convert_position(pos)
        if r.vis:
            array[index.y][index.x] = 2
        else:
            array[index.y][index.x] = 1

plot.imshow(array, interpolation='nearest')
plot.colorbar()
plot.show()

Pythonメモ

今回はクラスを使ってみた。

クラスについてはここを参考にした。

Python入門 - クラス

クラスメソッドについては、ここを参考にした。

uchikoshi22.hatenadiary.jp

余談

緯度・経度とlatitude, longitudeの覚え方。

d.hatena.ne.jp

道路や鉄道

土地利用細分メッシュでの森林の判定はイマイチだったので、とりあえず置いておいて、違う情報で道路や鉄道ができないか考える。

基盤地図情報の基本項目のデータ説明を見ると、道路の縁や軌道の中心線が取れるらしい。

軌道というのは鉄道の線路のことを指すらしい。

道路の縁ってあまり嬉しくないけど、軌道の中心線はありがたいのではないだろうか。

とりあえず、軌道の中心線を描けるように調べる。

なぜ普通の草が生やせないのか?

よく見る、この草。 f:id:pycra:20171011063356p:plain

見にくいので周りを土に変えて。 f:id:pycra:20171011063402p:plain

これが生やせない。

mcpipy/mcpi/block.pyを確認したけど、それらしいのはGRASS_TALL(31)FERN(31, 2)くらいしかない。

で、GRASS_TALLを指定すると、枯れた木?が生えた。

f:id:pycra:20171011063854p:plain

ブロックID=32が「枯れ木」なので、バグ?

ちなみに、32を指定すると、同じく「枯れ木」。 f:id:pycra:20171011064044p:plain

で、しょうがないので自分で草を設置して、ブロックID(データも)を確認するように、getBlockWithDataを呼ぶスクリプト(getblockwd.py)を作って確認。 (ついでにsetblock.pyもデータ対応した。)

草を設置して。 f:id:pycra:20171011063356p:plain

getblockwd.pyで調べると、(31, 1)とわかった。 f:id:pycra:20171011064709p:plain

隣にsetblock 1 0 0 31 1すると、同じ草が生えた! f:id:pycra:20171011064748p:plain

ということで、普通の草は(31, 1)で生やせる。

スクリプト

getBlockWithData対応のgetblockwd.py。

# coding: utf-8

import mcpi.minecraft as minecraft
import mcpi.block as block
import sys

FORMAT = "getBlockWithData({0:d}, {1:d}, {2:d})=({3:d}, {4:d})"

mc = minecraft.Minecraft()

if len(sys.argv) < 4:
    sys.exit("Usage: getblockwd.py [x y z]")

pos_x = int(sys.argv[1])
pos_y = int(sys.argv[2])
pos_z = int(sys.argv[3])

block = mc.getBlockWithData(pos_x, pos_y, pos_z)

mc.postToChat(FORMAT.format(pos_x, pos_y, pos_z, block.id, block.data))

引数のデータ対応のsetblock.py。

# coding: utf-8

import mcpi.minecraft as minecraft
import mcpi.block as block
import sys

FORMAT = "setBlock({0:d}, {1:d}, {2:d}, {3:d})"
FORMAT2 = "setBlock({0:d}, {1:d}, {2:d}, {3:d}, {4:d})"

mc = minecraft.Minecraft()

if len(sys.argv) < 5:
    sys.exit("Usage: setblock.py [x y z b d]")

pos_x = int(sys.argv[1])
pos_y = int(sys.argv[2])
pos_z = int(sys.argv[3])
block_id = int(sys.argv[4])
block_data = None
if len(sys.argv) >= 6:
    block_data = int(sys.argv[5])

if block_data is None:
    mc.setBlock(pos_x, pos_y, pos_z, block_id)
    mc.postToChat(FORMAT.format(pos_x, pos_y, pos_z, block_id))
else:
    mc.setBlock(pos_x, pos_y, pos_z, block_id, block_data)
    mc.postToChat(FORMAT2.format(pos_x, pos_y, pos_z, block_id, block_data))

余談

「GRASS_TALLってどんな植物?」ってググってみると、文字通り背の高い草が…「こんなにデカい草だったのね…ちょっとイメージが違う」

http://www.publicdomainpictures.net/pictures/20000/velka/tall-grass-1312079600krv.jpg

FERNについても調べたら、こっちはシダだった。

http://thepaintedhive.net/wp-content/uploads/2014/05/Free-Printable-Ferns.jpg