Pythonでマイクラを作る ⑱マインクラフトにワールドを転送する
Pythonでマイクラを作る 第18回目です。今回は本家マインクラフトとの連携を実装します。マイクラクローン(Pynecrafter)で作成した建築を本家マイクラに転送することができるようになります(トップ画像)。
まず本家マイクラにMOD(RemoteControllerMod)を導入して、マイクラクローンからコマンド(命令)を送信できるようにします。次に、マイクラクローン側にワールド転送プログラムを書いていきます。
RemoteControllerMod
本家マインクラフトとの連携の準備として、MODを導入したマインクラフト(Java Edition)を用意しなくてはなりません。MOD の導入については、Naohiro2g様の minecraft_remote を参照させていただきます。
今回は、マインクラフト(Java Edition)バージョン 1.12.2 を対象とします。必要なファイルは、2つです。リンク先からダウンロードしてください。
Forge 1.12.2
RemoteControllerMod-1.12.2 v0.02
Forge のダウンロード
Forge はMODを導入するための基本プログラムです。多くのMODは、Forge を土台とすることで、インストールして使用することができます。
「Download Recommended」はお薦めバージョンなので、こちらを使用します。
ダウンロードしたファイルをダブルクリックして、Forgeのインストールを開始します。「Install client」が選ばれていることを確認して、「OK」ボタンをクリックしてください。あとは自動でインストール処理が進みます。
「Successfully installed client profile ….」と表示されたら、インストールは完了です。
MODのダウンロード
MODのリンク先から、「Minecraft 1.12」の方を選んで、「ダウンロード」ボタンをクリックします。「ダウンロード」フォルダーに「RemoteController-0.02.jar」がダウンロードされていることを確認します。
次に、マインクラフトの設定を行います。
マインクラフトの設定
マインクラフト(Java Edition)を起動します。「起動構成」タブから「新規作成」ボタンをクリックしてください。
起動構成の編集画面が表示されます。
ゲームの名前は任意ですが、「1.12.2-forge」としました。
バージョンは、「release 1.12.2-forge-xxx」を選びます(先ほど、Forgeをインストールしたので、この項目が選べるようになっているのです)。
ゲームディレクトリも任意ですが、ゲームごとに別のディレクトリを作ることをお勧めします。今回は「ドキュメント」ディレクトリに「Minecraft」ディレクトリを作り、全てのゲームをここで管理することにしました。その中にゲームの名前と同じ「1.12.2-forge」というディレクトリを作って、そこをゲームディレクトリとして指定します。
全ての設定が終わったら、「保存」ボタンで保存します。
MODのインストール
マインクラフトの「プレイ」タブから、プレイ画面を表示します。
ゲームの名前「1.12.2-forge」を選んで、「プレイ」ボタンをクリックします。
ゲームが起動したら、「Quit Game」ボタンからゲームをそのまま終了します。次に、必要なMODをインストールします。
# ディレクトリ構造
Documents/
├ Minecraft/
│ ├ 1.12.2-forge/
│ │ ├ config/
│ │ ├ logs/
│ │ ├ mods/
│ │ │ ├ RemoteController-0.02.jar
│ │ │
│ │ ├ resourcepacks/
│ │ ├ saves/
│ │ ├ xxx
│ │ ├
│ │ ├
「ドキュメント」ディレクトリ→「Minecraft」ディレクトリ→「1.12.2-forge」ディレクトリを開きます。この中に「mods」ディレクトリが作成されているので、「RemoteController-0.02.jar」ファイルを配置します。
これでMODのインストール準備は完了です。もう一度、マインクラフトを起動して、ゲーム名「1.12.2-forge」を「プレイ」します。
MODがインストールされたマインクラフトが起動します。左下を見ると、Forge と MOD がインストールされていることが確認できます。
次に、ワールドを作成します。
ワールドの作成
ゲームが起動したら、「Singleplayer」ボタンから、ワールド選択画面に入ります。「Create New World」ボタンから、ワールドの作成を開始します。
ワールド名は任意で「New World」のままで進めます。Game Mode は「Creative」を選びます。「More World Options…」ボタンをクリックします。
面倒な平地化作業を省略するために、平面のみのワールド「Superflat」を選びます。「Create New World」ボタンから、新しいワールドを作成します。
ゲームが開始します。余計なブロックのない「スーパーフラット」ワールドが作成できました。これで準備は完了です。「エスケープ」キーからGame Menu画面に入って、「Save and Quit to Title」ボタンをクリックします。「Quit Game」ボタンで、マインクラフトを終了します。
mcpiライブラリのインストール
pipコマンドで「mcpi」ライブラリをインストールします。
Windowsの場合は、PowerShell を開いて、以下のコマンドを実行します。
Mac の場合は、ターミナルを開いて、以下のコマンドを実行します。
次は、マイクラクローン(Pynecrafter)側の操作になります。前回作成した村のワールドを本家マイクラに転送するプログラムを作成します。
ワールド転送プログラム
"""src/__init__.py"""
from .block import Block
from .player import Player
from .user_interface import UserInterface
from .inventory import Inventory
from .menu import Menu
from .architecture import Architecture
from .connect_to_mcpi import ConnectToMCPI # 追記
from .mc import MC
__init__.py はパッケージの初期化を行います。connect_to_mcpiモジュールから「ConnectToMCPIクラス」をインポートします。
"""src/mc.py"""
from direct.showbase.ShowBase import ShowBase
from panda3d.core import *
from . import *
class MC(ShowBase, UserInterface, Inventory, Menu, Architecture, ConnectToMCPI): # 修正
def __init__(self, ground_size=128, mode='normal'):
self.mode = mode
self.ground_size = ground_size
# ShowBaseを継承する
ShowBase.__init__(self)
self.font = self.loader.loadFont('fonts/PixelMplus12-Regular.ttf')
UserInterface.__init__(self)
Inventory.__init__(self)
Menu.__init__(self)
if self.mode == 'mcpi': # 追記
ConnectToMCPI.__init__(self) # 追記
以下略
MCクラスは、 全てのクラスをまとめて、ゲームを起動するクラスです。
MCクラスの引数に「ConnectToMCPIクラス」を追加して、継承します。そして、インスタンス変数mode が 'mcpi' のとき、ConnectToMCPIクラスの初期化メソッドを実行し、ワールドの転送機能が使えるようにします。
"""src/connect_to_mcpi.py"""
from mcpi.minecraft import Minecraft
class ConnectToMCPI():
# connection port for MCPI
PORT_MC = 4711
# block IDs for MCPI ## see block.py
AIR = 0
STONE = 1
GRASS_BLOCK = 2
DIRT = 3
COBBLESTONE = 4
OAK_PLANKS = 5
SAPLING = 6
BEDROCK = 7
WATER_FLOWING = 8
WATER = WATER_FLOWING
WATER_STATIONARY = 9
LAVA_FLOWING = 10
LAVA = LAVA_FLOWING
LAVA_STATIONARY = 11
SAND = 12
GRAVEL = 13
GOLD_ORE = 14
IRON_ORE = 15
COAL_ORE = 16
OAK_LOG = 17
OAK_LEAVES = 18
GLASS = 20
LAPIS_ORE = 21
LAPIS_BLOCK = 22
SANDSTONE = 24
BED = 26
RAIL_POWERED = 27
RAIL_DETECTOR = 28
COBWEB = 30
GRASS_TALL = 31
DEAD_BUSH = 32
WHITE_WOOL = 35
ORANGE_WOOL = (35, 1)
MAGENTA_WOOL = (35, 2)
LIGHT_BLUE_WOOL = (35, 3)
YELLOW_WOOL = (35, 4)
LIME_WOOL = (35, 5)
PINK_WOOL = (35, 6)
GRAY_WOOL = (35, 7)
LIGHT_GRAY_WOOL = (35, 8)
CYAN_WOOL = (35, 9)
PURPLE_WOOL = (35, 10)
BLUE_WOOL = (35, 11)
BROWN_WOOL = (35, 12)
GREEN_WOOL = (35, 13)
RED_WOOL = (35, 14)
BLACK_WOOL = (35, 15)
FLOWER_YELLOW = 37
FLOWER_CYAN = 38
MUSHROOM_BROWN = 39
MUSHROOM_RED = 40
GOLD_BLOCK = 41
IRON_BLOCK = 42
STONE_SLAB_DOUBLE = 43
STONE_SLAB = 44
BRICK_BLOCK = 45
TNT = 46
BOOKSHELF = 47
MOSS_STONE = 48
OBSIDIAN = 49
TORCH = 50
FIRE = 51
STAIRS_WOOD = 53
CHEST = 54
DIAMOND_ORE = 56
DIAMOND_BLOCK = 57
CRAFTING_TABLE = 58
FARMLAND = 60
FURNACE = 61
BURNING_FURNACE = 62
SIGN_STANDING = 63
DOOR_WOOD = 64
LADDER = 65
RAIL = 66
STAIRS_COBBLESTONE = 67
SIGN_WALL = 68
DOOR_IRON = 71
REDSTONE_ORE = 73
TORCH_REDSTONE = 76
SNOW = 78
ICE = 79
SNOW_BLOCK = 80
CACTUS = 81
CLAY = 82
SUGAR_CANE = 83
FENCE = 85
PUMPKIN = 86
NETHERRACK = 87
SOUL_SAND = 88
GLOWSTONE_BLOCK = 89
LIT_PUMPKIN = 91
STAINED_GLASS = 95
BEDROCK_INVISIBLE = 95
TRAPDOOR = 96
STONE_BRICK = 98
GLASS_PANE = 102
MELON = 103
FENCE_GATE = 107
STAIRS_BRICK = 108
STAIRS_STONE_BRICK = 109
MYCELIUM = 110
NETHER_BRICK = 112
FENCE_NETHER_BRICK = 113
STAIRS_NETHER_BRICK = 114
END_STONE = 121
WOODEN_SLAB = 126
STAIRS_SANDSTONE = 128
EMERALD_ORE = 129
RAIL_ACTIVATOR = 157
LEAVES2 = 161
TRAPDOOR_IRON = 167
FENCE_SPRUCE = 188
FENCE_BIRCH = 189
FENCE_JUNGLE = 190
FENCE_DARK_OAK = 191
FENCE_ACACIA = 192
DOOR_SPRUCE = 193
DOOR_BIRCH = 194
DOOR_JUNGLE = 195
DOOR_ACACIA = 196
DOOR_DARK_OAK = 197
GLOWING_OBSIDIAN = 246
NETHER_REACTOR_CORE = 247
PISTON = 33
JACK_O_LANTERN = 91
def __init__(self):
print('connect to mcpi')
self.mc = Minecraft.create(port=ConnectToMCPI.PORT_MC)
self.base_position = self.mc.player.getPos()
self.accept('m', self.transport_blocks_to_mcpi)
self.accept('n', self.clear_all_blocks)
def transport_blocks_to_mcpi(self):
self.mc.postToChat('transport_blocks_to_mcpi')
player_position = self.player.position
self.mc.player.setPos(
self.base_position.x - player_position.x,
self.base_position.y + player_position.z,
self.base_position.z + player_position.y
)
for key, block_id in self.block.block_dictionary.items():
x, y, z = [int(value) for value in key.split('_')]
block_id_mcpi = self.get(block_id.upper()) or 1
if isinstance(block_id_mcpi, int):
self.mc.setBlock(
self.base_position.x - x,
self.base_position.y + z,
self.base_position.z + y,
block_id_mcpi
)
else: # colored wool
self.mc.setBlock(
self.base_position.x - x,
self.base_position.y + z,
self.base_position.z + y,
*block_id_mcpi
)
def clear_all_blocks(self):
self.mc.postToChat('clear_all_blocks')
base_position = self.mc.player.getPos()
self.mc.setBlocks(
base_position.x - 100,
base_position.y,
base_position.z - 100,
base_position.x + 100,
base_position.y + 100,
base_position.z + 100,
0 # AIR
)
self.mc.setBlocks(
base_position.x - 100,
base_position.y - 1,
base_position.z - 100,
base_position.x + 100,
base_position.y - 2,
base_position.z + 100,
2 # GRASS
)
srcディレクトリの中に「connect_to_mcpi.py」ファイルを作成し、ワールド転送プログラムを書いていきます。
ConnectToMCPIクラスは、マイクラクローンと本家マイクラをつなぎ、各種コマンドで本家マイクラを操作できるようにするクラスです。
1行目で、mcpi.minecraftモジュールから、Minecraftクラスをインポートします。
このクラスには、クラス変数をたくさん設定しなければなりません。PORT_MC はポート番号を指定しています。MOD導入済みのマイクラはコマンドを受けつけるサーバーを開いていますが、そのサーバーに接続するための番号が「4171」番になります。
クラス変数AIR = 0 以降が、ブロックIDの指定です。マイクラクローンでは、ブロックID は「stone」など、文字列で表されます。一方、マイクラ(バージョン1.12.2)では、数字で表されます。これらのクラス変数を設定することで、ブロックIDの変換ができるようにします。
初期化メソッド(__init__)を設定します。
はじめに、Minecraft.createメソッドで、マイクラクローンと本家マイクラの接続を開始します。引数port は ConnectToMCPI.PORT_MC(4171)番を指定します。
次に、mc.player.getPosメソッドで本家マイクラ側のプレイヤーの位置を取得して、インスタンス変数base_position に代入します。ブロックやプレイヤーは、base_position を位置の基準として配置していくことになります。
acceptメソッドで、ユーザーのキー操作をゲーム側で受け取れるようにします。「M」キーで、マイクラクローン側の建築を本家マイクラに転送するコマンドを送信します(transport_blocks_to_mcpiメソッド)。「N」キーで、本家マイクラの建築を全て削除して、元のスーパーフラットワールドに戻るコマンドを送信します(clear_all_blocksコマンド)。
transport_blocks_to_mcpiメソッド
transport_blocks_to_mcpiメソッドは、マイクラクローン側で設置したブロックを全て本家マイクラにコピーできます。
for文を使って、マイクラクローンに設置したブロックを本家マイクラに一つずつ設置するコマンド(mc.setBlockメソッド)を送信します。インスタンス変数 block.block_dictionary からキーと値を取得します。
キーはブロックの位置情報を含みます。「_(アンダーバー)」で分割して、ブロックの座標(x, y, z)を得ることができます。
値はブロックIDです。getメソッドで、クラス変数にアクセスして、「文字列」→「数値」の変換を行い、本家マイクラのブロックIDを得ることができます。ここで注意してほしいのは、本家マイクラの羊毛ブロックの IDは (35, 1) のように2つの数字で指定する必要があることです。色によって、2つ目のIDの値が変わります。条件式「if isinstance(block_id_mcpi, int):」により場合分けをして、ブロックID がタプルであったときは、「*block_id_mcpi」のように「*(アスタリスク)」をつけて展開して指定するようにします。
本家マイクラとマイクラクローン(Panda3D)の座標変換が必要です。上図の左がマインクラフト、右が Panda3Dの座標系です。
# ブロックを設置する部分のコード
self.mc.setBlock(
self.base_position.x - x,
self.base_position.y + z,
self.base_position.z + y,
block_id_mcpi
)
したがってマイクラ座標とPanda3D座標の変換は x → -x、y → z、z → y、とすることで、正しい方向でワールドが転送できます。
(ちなみに本家マイクラでは、X座標が東、Z座標が南と方角が設定されています。豆知識)
clear_all_blocksメソッド
clear_all_blocksメソッドは、本家マイクラのワールドを初期化します。建築したブロックを全て削除してやり直したい時に使用します。mc.setBlocksメソッドは、範囲を指定してブロックを設置できます。プレイヤーを中心に縦横200、高さ100にAIR(空気)ブロックを設置して、範囲内のブロックを全て消すことができます。それから高さ-1に200x200のGRASS_BLOCK(草)ブロックを設置して地面を復元します。
転送するワールドを作成する
"""18_01_main.py"""
from math import *
from random import randint, choice
from panda3d.core import *
from src import MC
class Game(MC):
def __init__(self):
# MCを継承する
MC.__init__(self, ground_size=256, mode='mcpi')
# プレイヤーの位置を変更
self.player.position = Point3(5, -30, 0)
# 座標軸
self.axis = self.loader.loadModel('models/zup-axis')
self.axis.setPos(0, 0, 0)
self.axis.setScale(1.5)
self.axis.reparentTo(self.render)
# 壁
for i in range(4):
for j in range(3):
if i == 0:
self.block.add_block(i, 0, j, 'stone')
else:
self.block.add_block(i, 0, j, 'gold_block')
# 道路
self.make_road(initial_position=Point3(-64, 0, 0))
self.make_road(length=64, initial_position=Point3(9, -32, 0), angle=90)
self.make_road(length=32, initial_position=Point3(9, 30, 0), angle=45)
self.make_road(length=32, initial_position=Point3(9, 35, 0), angle=135)
# 街路樹
for i in range(-101, 100, 10):
for j in [-1, 9]:
self.make_tree(initial_position=Point3(i, j, 0))
# 家
count = 0
roof_colors = ['red', 'blue', 'green', 'pink', 'gray']
for i in range(-51, 40, 30):
for j in [-21, 30]:
w = randint(8, 10)
d = randint(8, 10)
h = randint(8, 10)
x = i + (21 - w) // 2
if j == -21:
y = j
else:
y = j - d
# roof_block_id = choice(roof_colors) + '_wool'
roof_block_id = roof_colors[count % len(roof_colors)] + '_wool'
self.make_house(initial_position=Point3(x, y, 0), w=w, d=d, h=h, roof_block_id=roof_block_id)
count += 1
game = Game()
game.run()
18_01_main.py は自動で村を建築するプログラムです。
「MC.__init__(self, ground_size=256, mode='mcpi')」により、MCクラスの初期化メソッドを実行しますが、引数mode を 'mcpi' に指定します。そうすることで、本家マイクラとの接続が実行されます。村を作成する部分は、17_01_main.py とほぼ同じです。屋根の色のランダム要素を無くして、1件ずつ色が変わるように変更しました。
ワールドの転送を実行する
では、ワールドの転送実験を開始します。
まず、本家マイクラ「1.12.2-forge」を起動します。先ほど作成した「New World」でゲームを開始します。「/」キーを押すと、マイクラ画面を開いたまま別のウインドウに移れるので便利です。覚えておきましょう。
次に、マイクラクローン(Pynecrafter)を実行します。18_01_main.py を実行して、村の自動建築を行います。「M」キーを押すと、本家マイクラにワールドが転送されました。「N」キーで全ての建築を削除できることも確認しておいてください。
今回は、マイクラクローンと本家マイクラの連携について実験しました。「RemoteControllerMod」と「mcpi」ライブラリを使うと、簡単にワールドの転送が行えました。マイクラクローン側で試行錯誤して作ったワールドを本家マイクラに転送するなど、建築作業の効率化にお役立てください。
次回は、ゲームに音楽(ミュージック)を導入します。BGMとプレイヤーの動作音(ブロックの破壊音など)を鳴らすことができるようになります。お楽しみに。
前の記事
Pythonでマイクラを作る ⑰建築MODで村を作る
次の記事
Pythonでマイクラを作る ⑲サウンドを実装する
その他のタイトルはこちら
この記事が気に入ったらサポートをしてみませんか?