月曜日, 6月 9, 2025
- Advertisment -
ホームニューステックニュースTypeScriptでマイクラやってみた

TypeScriptでマイクラやってみた


統合版マイクラには Script API という機能があり、JavaScriptで既存のマイクラのしくみを変えるアドオンを作成ことができます。TypeScriptでの開発にも対応しています。

https://learn.microsoft.com/en-us/minecraft/creator/documents/scriptinggettingstarted?view=minecraft-bedrock-stable

TypeScriptを使ってのScriptAPIの環境開発構築と
One Block Challenge風のブロックを壊すとランダムに新しいブロックが生成されるしくみ
を作成してみました

用語解説

マイクラには主に統合版Java版があり
見た目は似ているのですが内部的な機能が全く異なります

紛らわしいので用語を整理します

アドオン

統合版マイクラの機能
世界の見た目を変えるリソースパックresource_pack
世界の振る舞いを変えるビヘイビアーパックbehavior_packからなる
本記事で取り扱うScript APIもアドオンの機能のひとつ

データパック

Java版マイクラの機能
コマンドやjsonファイルで世界の振る舞いを変えられる
見た目を変えるにはリソースパックと併用する

mod

Java版マイクラに追加する拡張機能のプログラムのこと
Javaなどのプログラミング言語を使って開発をおこなう
Script APIとは全く関係ありません

開発環境

本記事は下記バージョンで動作確認をおこなっています

  • windows 11
  • 統合版マイクラ 1.21.82
  • VScode 1.100.2
  • Node.js v22.11.0

これらはインストール済みの状態から、開発環境を整えます

環境構築

プロファイルの作成

コードエディタのVScodeで、統合版マイクラ開発用のプロファイルを作成します

VScodeを起動します
プロファイルの選択
プロファイルの選択

左下の歯車マークから設定を開き、プロファイル->プロファイルを選択します
画像は既に設定済みのためアイコンが変わっています

名前の設定
名前の設定

名前とアイコンをお好みで設定します
チェックボタンをクリックして環境をアクティブにします

mcpeという呼び名は昔統合版マイクラがスマホ向けのポケットエディションPEから開発が始まったことに由来します
なおJava版のデータパック開発環境とはプロファイルを分けておかないと
mcfunctionファイルの文法チェックが競合します

拡張機能導入

統合版マイクラ用のプロファイルを作成してアクティブ化しましたら
VScodeの拡張機能を導入します

画面左側のメニュー、四角を積み重ねているアイコンの拡張機能を表示します

拡張機能一覧
インストール済みの拡張機能一覧

Marketplaceで拡張機能を検索するの欄に拡張機能名をコピペして検索、インストールしてください

統合版マイクラ用の拡張機能

Bedrock Definitions
統合版マイクラ用オートコンプリート
用語の入力をサポート

https://marketplace.visualstudio.com/items?itemName=destruc7i0n.vscode-bedrock-definitions

VSCode-Bedrock-Development-Extension
統合版マイクラ開発用拡張機能

https://marketplace.visualstudio.com/items?itemName=BlockceptionLtd.blockceptionvscodeminecraftbedrockdevelopmentextension

Minecraft Bedrock Debugger
デバッカー
マイクラと接続してデバッグする方法は拡張機能の説明をご覧ください

https://marketplace.visualstudio.com/items?itemName=mojang-studios.minecraft-debugger

便利な拡張機能

Prettier – Code formatter
コードフォーマッター
コードを自動で整えます

https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

Code Spell Checker
英語のスペルミスを指摘します

https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker

マイクラ用語もスペルミス判定されますが
クイックフィックス→Add xxx to workspace settingで回避できます

ネザーラック
マイクラ用語のスペルミス判定

クイックフィックスの選択肢
Add xxx to workspace settingを選択

以上で開発環境の準備は終了です

TypeScriptでマイクラをやってみる

TypeScriptスターターサンプルプロジェクトを利用して、実際にアドオンを作成してマイクラに反映させるまでの手順を確認します

https://learn.microsoft.com/en-us/minecraft/creator/documents/scriptinggettingstarted?view=minecraft-bedrock-stable

スタータープロジェクトの準備

公式のGitHubリポジトリより、プロジェクトのファイルをダウンロードします
プロジェクトにアクセスします

https://github.com/microsoft/minecraft-scripting-samples/

Codeのプルダウンより、Download ZIPを選択し、zipファイルをダウンロードします

code
Download ZIPを選択

ダウンロードしたzipファイルを展開し、ts-starterフォルダを任意の場所にコピーします
ts-starterフォルダを元に開発を進めます

ts-starter
ts-starterフォルダの中身

コピーしたts-starterフォルダをエクスプローラーで表示し
SHIFTを押しながら右クリックでメニューを表示します
codeで開く

codeで開くを選択します

もし、codeで開くの選択肢が表示されない場合は別の方法で
ts-starterフォルダをVScodeで開いてください

フォルダを開く
ファイルからフォルダを開く

プロジェクトの復元

ts-starterプロジェクトで利用されているパッケージをインストールします

VScodeで新しいターミナルを表示します
上部メニューより、ターミナル→新しいターミナルを選択します

新しいターミナルの表示

ターミナルに入力します

パッケージがインストールされます

環境変数の設定

環境変数を設定します
.envファイルを開きます

VScode左側のファイルのアイコンで、開いているフォルダに存在するファイルが一覧表示されます
.envファイルをクリックして表示します

env
.envファイル

PROJECT_NAMEはこのアドオンのフォルダ名になります
behavior_packsresource_packs配下のフォルダ名と一致させる必要があります

フォルダ名は、フォルダ名を右クリック→名前の変更
またはショートカットキーF2で変更できます

他はデフォルトのままにします

manifest.json

マニュフェストファイルを編集します
作成するアドオンの名前や説明などを記載します

behavior_packsresource_packs配下にそれぞれ1ファイルずつあります

behavior_packs\sumiso_sample_ts\manifest.json

{
  "format_version": 2,
  "header": {
    "name": "sumiso_sample_ts", 
    "description": "TypeScriptでマイクラやってみた", 
    "uuid": "3a5c17cd-bcc7-4754-b993-47df5a4765d1", 
    "version": [1, 0, 0],
    "min_engine_version": [1, 20, 30]
  },
  "modules": [
    {
      "description": "Script resources",
      "language": "javascript",
      "type": "script",
      "uuid": "e8b1d741-eb59-421d-a302-7d6e2ba66030", 
      "version": [1, 0, 0],
      "entry": "scripts/main.js"
    }
  ],
  "dependencies": [
    {
      "module_name": "@minecraft/server",
      "version": "1.8.0"
    },
    {
      "uuid": "fa1f7c90-5963-4c10-8bea-069f14aa9c92", 
      "version": [1, 0, 0]
    }
  ]
}

resource_packs\sumiso_sample_ts\manifest.json

{
  "format_version": 2,
  "header": {
    "name": "sumiso_sample_ts", 
    "description": "TypeScriptでマイクラやってみた", 
    "uuid": "fa1f7c90-5963-4c10-8bea-069f14aa9c92", 
    "version": [1, 0, 0],
    "min_engine_version": [1, 20, 30]
  },
  "modules": [
    {
      "description": "My Resource Pack",
      "type": "resources",
      "uuid": "cd721294-e4b6-42a0-afb0-aad512124730", 
      "version": [1, 0, 0]
    }
  ],
  "dependencies": [
    {
      "uuid": "3a5c17cd-bcc7-4754-b993-47df5a4765d1",  
      "version": [1, 0, 0]
    }
  ]
}

それぞれheader部分のnameとdescriptionを記載します
header部分の内容がマイクラの画面から確認可能です

世界を編集
headerで設定した内容がマイクラの画面に反映されます

uuidはこのアドオン固有のIDです
適当な生成サイトを利用して、uuidを生成して書き換えます

https://www.uuidgenerator.net/

こちらのサイトでは、更新するたび新しいuuidが生成されます
コピーして、uuidの部分を貼り替えます
headerとmodulesの合計4か所変更します

uuid
画面を更新すると新しいuuidが生成されます

dependenciesの部分は
ビヘイビアパック→リソースパックのheaderのuuid
リソースパック→ビヘイビアパックのheaderのuuid
をそれぞれ指定します

依存関係を設定することで
マイクラのワールド設定で、ビヘイビアパックまたはリソースパックを有効化すると
対応するパックも自動的に有効化されるようになります

依存関係の指定をミスってると、対応するパックがないと注意が表示されます(1敗)

main.ts

typescriptのコード本体は、scriptsのmain.tsに書かれています

main.ts

import { world, system } from "@minecraft/server";

function mainTick() {
  if (system.currentTick % 100 === 0) {
    world.sendMessage("Hello starter! Tick: " + system.currentTick);
  }

  system.run(mainTick);
}

system.run(mainTick);
  1. mainTick関数を定義
  2. system.runで実行

というシンプルなコードが記載されています

mainTick関数の最後に、再度system.run(mainTick)が呼び出されてます
そのためこの関数は毎tickごとに実行されます

1tickはゲームが処理される最小の時間です
統合版マイクラは20tickで1秒
100tickで5秒になります

したがってmainTickでは
5秒ごとにメッセージを送信する処理になっています

実際にマイクラで動作確認を行います

デプロイ

.envファイルの確認および、manifest.jsonを編集したら
デプロイしてマイクラで利用できるか確認します

VScodeのターミナルでデプロイを実行します

typescriptのコードがjavascriptにコンパイルされ
下記フォルダに配置されます

ビヘイビアーパック
%LOCALAPPDATA%\Packages\Microsoft.MinecraftUWP_8wekyb3d8bbwe\LocalState\games\com.mojang\development_behavior_packs

リソースパック

%LOCALAPPDATA%\Packages\Microsoft.MinecraftUWP_8wekyb3d8bbwe\LocalState\games\com.mojang\development_resource_packs

統合版マイクラを起動して、テスト用のワールドを作成します

ゲームモードはクリエイティブ
詳細設定で平坦な世界を有効化
オーバーワールドのプリセットを選択します

平坦な世界

チートを有効化します
リソースパックに、デプロイしたパックが表示されます
パックを有効化します

世界を編集

ビヘイビアーパックでも有効化されていることを確認します

そのほかの設定はお好みで
ワールドを作成します

表示確認
typescriptでマイクラに干渉する

5秒ごとにメッセージが表示されることが確認できました

One Block Challenge風ブロック作成

typescriptで統合版マイクラのアドオンが作成できることが確認できました

このファイルを改造してOne Block Challenge風の
壊してもランダムな種類のブロックが無限に再生成されるブロック
を作ってみます

要件定義

One Block Challengeとは次のようなマイクラの配布マップです

よくあるOne Block Challenge

- はじめ1 ブロックだけあり、その上にスポーン
- 1 ブロックを壊すとランダムに新しいブロックが生成される
- 一定数ブロックを壊すと、チェストが出現してアイテムが入手できる
- 一定数ブロックを壊すと、ステージが上がって出現するブロックの種類が変わる
- 最終的にエンドラ討伐できる

もっともコアな機能

1 ブロックを壊すとランダムに新しいブロックが生成される

を実装します
以下この機能をブロック管理と呼びます

詳細設計

ブロック管理機能を分解すると、次の関数に分けられます

  • ブロックプールの定義
  • ブロック破壊検知
  • 無限生成ブロックか判定
  • ランダムブロックの選択
  • ブロック更新

ブロックプールは、次に生成されるブロックの抽選対象です
マイクラ内にあるすべてのブロックがランダム生成されると
サバイバルで詰んでしまうので
指定したブロックからランダムに生成させます

実装

ブロック破壊検知以外の、
ブロックの管理機能をblock.tsにまとめました

block.ts

import { Vector3, world, BlockPermutation, Block } from "@minecraft/server";

const overworld = world.getDimension("overworld");

const blockPool: { [stage: number]: { block: string; weight: number }[] } = {
  1: [
    { block: "minecraft:grass", weight: 40 },
    { block: "minecraft:dirt", weight: 30 },
    { block: "minecraft:stone", weight: 20 },
    { block: "minecraft:oak_log", weight: 10 },
  ],
};


function getRandomBlock(stage: number): string {
  const pool = blockPool[stage] || blockPool[1]; 
  const totalWeight = pool.reduce((sum, entry) => sum + entry.weight, 0);
  let randomValue = Math.random() * totalWeight;

  for (const entry of pool) {
    if (randomValue  entry.weight) {
      return entry.block;
    }
    randomValue -= entry.weight;
  }

  return "minecraft:dirt";
}


export function isTargetBlock(block: Block) {
  const TargetBlockPosition: Vector3 = { x: 0, y: 0, z: 0 };
  return (
    block.dimension === overworld &&
    block.location.x === TargetBlockPosition.x &&
    block.location.y === TargetBlockPosition.y &&
    block.location.z === TargetBlockPosition.z
  );
}


export function updateBlock(block: Block) {
  const nextBlockId = getRandomBlock(1); 
  const blockPermutation = BlockPermutation.resolve(nextBlockId);

  block.setPermutation(blockPermutation);
}

typescriptで書けるため、型ヒントをつけて型安全に開発が進められます
だいたいCopilotに生成させたのち、直しています
詳しく見ていきます

ブロックプールの定義
次に生成するブロックの抽選対象です
将来的にstage対応できるような構造になっています

const blockPool: { [stage: number]: { block: string; weight: number }[] } = {
  1: [
    { block: "minecraft:grass", weight: 40 },
    { block: "minecraft:dirt", weight: 30 },
    { block: "minecraft:stone", weight: 20 },
    { block: "minecraft:oak_log", weight: 10 },
  ],
};

ランダムブロックの選択
ブロックプールからランダムにブロックを選択して
minecraft:dirtのようなコマンドで指定する
ブロック名を返します

Copilotに生成させましたが、ルーレット選択になってるらしいです


function getRandomBlock(stage: number): string {
  const pool = blockPool[stage] || blockPool[1]; 
  const totalWeight = pool.reduce((sum, entry) => sum + entry.weight, 0);
  let randomValue = Math.random() * totalWeight;

  for (const entry of pool) {
    if (randomValue  entry.weight) {
      return entry.block;
    }
    randomValue -= entry.weight;
  }

  return "minecraft:dirt";
}

無限生成ブロックか判定
ブロック破壊検知では、どこのブロックが壊されてもイベントが発生します
無限再生成するのはOne Blockだけなので、対象の座標にあるか確認します

const overworld = world.getDimension("overworld");

export function isTargetBlock(block: Block) {
  const TargetBlockPosition: Vector3 = { x: 0, y: 0, z: 0 };
  return (
    block.dimension === overworld &&
    block.location.x === TargetBlockPosition.x &&
    block.location.y === TargetBlockPosition.y &&
    block.location.z === TargetBlockPosition.z
  );
}

Vector3はマイクラの座標を表現する型です
またオーバーワールド以外のディメンション、たとえばネザーの同じ座標のブロックを壊したときに判定されないように
ディメンションも確認しています

ブロック更新
ブロックを設置するためにはまずBlockPermutationを取得する必要があります
ブロックの名前と状態の組み合わせです

ブロック破壊検知で取得したBlockに対して、ブロックの設置を行っています

export function updateBlock(block: Block) {
  const nextBlockId = getRandomBlock(1); 
  const blockPermutation = BlockPermutation.resolve(nextBlockId);

  
  block.setPermutation(blockPermutation);
}

レベルアップしてstageを切り替える機能はないのでstage1固定です

メインの処理、main.tsはシンプルにブロック破壊検知用のイベント登録を行っています

main.ts


import { world, PlayerBreakBlockAfterEvent } from "@minecraft/server";
import { updateBlock, isTargetBlock } from "./block";


function breakBlockEventHandler(e: PlayerBreakBlockAfterEvent) {
  if (isTargetBlock(e.block)) {
    updateBlock(e.block);
  }
}


world.afterEvents.playerBreakBlock.subscribe(breakBlockEventHandler);

型ヒントで何のイベントを登録しているか大変分かりやすい

https://learn.microsoft.com/en-us/minecraft/creator/scriptapi/minecraft/server/playerbreakblockafterevent?view=minecraft-bedrock-stable

イベントハンドラーでは
壊したブロックが無限生成の対象ブロックか判定し
対象であればブロックを更新しています


function breakBlockEventHandler(e: PlayerBreakBlockAfterEvent) {
  if (isTargetBlock(e.block)) {
    updateBlock(e.block);
  }
}

イベントにはBeforeEventとAfterEventがあります
ここではブロックが破壊されたあとに再生成したので
AfterEventを利用しています

world.afterEvents.playerBreakBlock.subscribe(breakBlockEventHandler);

BeforeEventだと、イベントのキャンセル
たとえば絶対に壊させないブロックが作成できます
しかし一瞬壊れて再生成されているように見えます

デプロイ

コードを作成したらデプロイします

ホットリロード対応の記載が公式のチュートリアルにはありますが
機能していない様子
毎回コマンドをたたいてデプロイします

動作確認

最初に作ったスーパーフラットのワールドで
座標(0,0,0)にブロックを置き
ブロックを壊すと再生成されるか確認します

https://youtu.be/NPvN4ux6vZ0

もしこの開発中もマイクラのワールドを開きっぱなしで
一時停止にしていたのなら
マイクラ内のコマンドで

を実行してください
デプロイしたアドオンが再読み込みされます

ワールドに入りなおしてもOK

以上で、One Block Challenge風ブロック管理のしくみができました

終わりに

TypeScriptを使ってのScriptAPIの環境開発構築と
One Block Challenge風のブロックを壊すとランダムに新しいブロックが生成されるしくみ
を作成してみました

公式のチュートリアルではテラコッタを壊すゲームcottaを作りながら

  • for文を用いたブロックの設置
  • スコアボード管理
  • モンスターの召喚

などを学べますのでオススメです

https://learn.microsoft.com/en-us/minecraft/creator/documents/scriptinggettingstarted?view=minecraft-bedrock-stable



Source link

Views: 0

RELATED ARTICLES

返事を書く

あなたのコメントを入力してください。
ここにあなたの名前を入力してください

- Advertisment -