main() blog

プログラムやゲーム、旅、愛する家族について綴っていきます。

【pyxel】Webから外部データを取得してみよう!

概要

『ムゲンヒメクリ』の日本の祝日に対応するために祝日の情報をウェブから取得して表示するようにしました。
ローカルにデータを用意して読み込ませるでも良かったのですが、
どうせなら公開されているデータを取得して反映させるようにすることで、 データの更新も不要になります。

内閣府の「国民の祝日」のデータは以下で公開されています。
こちらはcsvでデータがアップされている様です。

https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv

また、以下のGitHubにも日本の祝日のデータがアップされている様です。
holidays-jp.github.io

以下のURLでjsonデータで取得することができます。
https://holidays-jp.github.io/api/v1/date.json

公開されているデータはどちらも2年間のみのものとなっています。

jsonファイルの取得方法

標準ライブラリのurllib.requestもしくはrequestsライブラリを使用して指定したURLからデータを取得する形になります。
今回は標準ライブラリのurllib.requestを使用して実装してみたいと思います。

   # 日本の祝日のデータの取得.
    def fetch_jp_holidays(self):
        # urllibで試してみる.
        url = "https://holidays-jp.github.io/api/v1/date.json"

        try:
            import urllib.request
            with urllib.request.urlopen(url) as response:
                if response.status == 200:
                    import json
                    self._holidays = json.load(response)
                else:
                    print(f"サーバエラー: ステータスコード {response.status}")
                    pass
        except Exception as e:
            print(f"祝日のデータの取得に失敗: {e}")
            pass          

urllib.requests.urlopen()でURLを指定することでjsonデータを取得することができます。
response.statusの値でデータ取得の成功、失敗の判定が行えます。
200が返ってくると成功なのでjson.load()でデータを取得できます。

ブラウザ版

ブラウザ版の場合はこの方法ではデータの取得は行えない様です。
ブラウザ版Pyxelは、内部で Pyodide(Pythonをブラウザで動かす仕組み)を使用しています。
このPyodide上のPythonには、標準の urllib でネットワーク通信(HTTPS)を行う機能が制限されているという制約があります。
そのため、https://... というURLを理解できずにエラーになってしまいます。

pyodide.http.pyfetchを使用することでブラウザ版の処理を実装してみます。

   # 日本の祝日のデータの取得.
    def fetch_jp_holidays(self):
        # urllibで試してみる.
        url = "https://holidays-jp.github.io/api/v1/date.json"

        if IS_BROWSER:
            # ブラウザ版: pyodide.http.pyfetch を非同期で実行
            #import pyodide_js
            import asyncio

            async def download():
                try:
                    from pyodide.http import pyfetch
                    # pyfetch を使う(こちらの方がPyodideでは推奨されます)
                    response = await pyfetch(url)

                    if response.status == 200:
                        # await を忘れずに
                        js_obj = await response.json()
                        self._holidays = js_obj
                        #print(f"祝日のデータを取得: {len(self._holidays)}件")
                        #print(f"祝日のデータの例: {self._holidays}")
                        self._is_loading = False
                    else:
                        print(f"サーバエラー: ステータスコード {response.status}")  
                        self._is_loading = False
                except Exception as e:
                    print(f"祝日のデータの取得に失敗: {e}")
                    self._is_loading = False

            # asyncioのイベントループにタスクを投げる(プログラムは止めない)
            asyncio.create_task(download())
        else:
            try:
                import urllib.request
                with urllib.request.urlopen(url) as response:
                    if response.status == 200:
                        import json
                        self._holidays = json.load(response)
                        #print(f"祝日のデータを取得: {len(self._holidays)}件")
                        #print(f"祝日のデータの例: {self._holidays}")
                        self._is_loading = False
                    else:
                        print(f"サーバエラー: ステータスコード {response.status}")
                        self._is_loading = False
                        pass
            except Exception as e:
                print(f"祝日のデータの取得に失敗: {e}")
                self._is_loading = False
                pass

注意点としてブラウザ版ではダウンロード処理は非同期処理で実装する必要があることです。
urllibを使用した実装の時はその場で結果を待って成功、失敗を処理していました。
これはurlopenを実行した際に同期処理で行われるためダウンロードが完了するまでブロックしていまします。
今回はデータ量が小さいため気になりませんが、データ量が多い場合は処理が停止してしまいます。
ブラウザ版のpyfetchは非同期で処理されるため、ゲーム側はブロックされませんが、
完了時のコールバック関数などを用意してデータ取得後の処理などを実祖する必要が出てきます。

今回は祝日のデータでしたが、他のデータでも基本的な対応は同じになると思いますので、
必要なデータに合わせて適宜実装していただければと思います。

参考

github.com


【pyxel】無限にめくれる!?「ムゲンヒメクリ」公開!

pyxelで作成したゲーム?「ムゲンヒメクリ」を公開しました!
(ひっそりタイトル名を変更しました!)

無限(?)にめくれる日めくりカレンダーとなっています。
Eテレの0655、2355を見ていて、ふと作ってみました。

ブラウザ版はこちらのリンクから遊ぶことができます。
スマホでも遊ぶことができるので、ぜひ遊んでみてください。

https://takezoh-1127.github.io/pyxel-public-daily/

操作方法

スマホであればタッチして下方向(若干、左右方向)にフリックでめくれます。
PCはマウスで同様の操作、もしくはスペースキーでめくれます。

更新履歴

日付 内容
2026/2/23 日本の祝日に対応
2026/2/22 フリックの調整と一定時間操作がない時にフリック操作を促すアイコン表示の追加
2026/2/18 公開

参考

github.com


【pyxel】セーブ/ロード機能を実装しよう!

概要

pyxelのゲームにセーブロード機能を実装方法についての紹介です。
pyxelではセーブロードを行う機能は提供されていません。
ですのでプロジェクトで独自に対応する必要があります。
今回はjson形式やpickleというモジュールでのセーブロードの方法について解説します。

テストプロジェクトのソースはgithubの以下のURLで公開しています。

github.com

セーブデータ構成

テストプロジェクトではゲームのセーブデータを想定して以下のデータを定義しています。
ゲームではよくシステムデータとゲームデータを分けてセーブを行ったりしますが、
今回はテスト実装ということもありセーブデータ一つに集約しています。

# セーブデータ.
# システムデータとゲームデータを分けたりするが、ここではまとめて扱う.
class SaveData:
    def __init__(self):
        self._version = 1
        self._option_data = OptionData()
        self._game_data = GameData()
        self._record_data = RecordData()
        pass

# 戦歴などの記録データ.
class RecordData:
    LOG_MAX = 100

    def __init__(self):
        self._play_time = 0

        # ENEMY_BEGIN から ENEMY_END までの討伐記録データ.
        self._enemy_record = {CharacterID(v): RecordEnemyData() for v in range(CharacterID.ENEMY_BEGIN.value, CharacterID.ENEMY_END.value + 1)}

        # ログデータ.
        # テキスト(文字列)の可変長配列(上限は100件).
        self._log_data = []
        pass

# 敵キャラクターの記録データ.
class RecordEnemyData:
    def __init__(self):
        self._kill_count = 0
        pass


# ゲームデータ.
class GameData:
    def __init__(self):
        # CHARA_BEGIN から CHARA_END までのキャラクターデータ.
        # 範囲内の全キャラクターを初期化しておく.
        self._characters = {CharacterID(v): CharacterData(CharacterID(v)) for v in range(CharacterID.CHARA_BEGIN.value, CharacterID.CHARA_END.value + 1)}
        pass

# オプションデータ.
class OptionData:
    MIN_VOLUME = 0
    MAX_VOLUME = 10

    def __init__(self):
        self._volume_se = 5
        self._volume_voice = 5
        self._volume_bgm = 5

        self._language = Language.JP
        self._difficulty = Difficulty.DEFAULT
        pass

# キャラクターデータ.
class CharacterData:
    def __init__(self, chara_id = CharacterID.NONE):
        self._chara_id = chara_id           # キャラクタID.
        self._name = ""
        self._level = 1
        self._exp= 0
        self._hp = 0
        self._mp = 0
        pass

セーブデータの保存先

pyxelの以下の関数でプラットフォームに適した保存先のパスを返してくれます。

pyxel.user_data_dir(vendor, app_name)

vender、app_nameを引数で与えることでそれぞれのフォルダの階層のフォルダが作成されます。
venderを"takezoh"、app_nameを"pyxel_save_load_sample"とした場合、Macでは以下のフォルダが作成されます。

開発中は実行環境直下に保存しても良いと思います。
後述しますが、ブラウザ版ではこちらのパスを使用しても保存できない様なので、別の実装が必要となります。

json

jsonファイルとして出力する方法は以下で行います。

import json
import os

def save_to_file(self, path):
    # ensure directory exists
    dirname = os.path.dirname(path)
    if dirname:
        os.makedirs(dirname, exist_ok=True)
    with open(path, "w", encoding="utf-8") as f:
        json.dump(self.to_dict(), f, ensure_ascii=False, indent=2)

json形式で出力する際にjson.dump()の関数を使用しますが、dictとしてデータを用意する必要があります。
今回用意したSaveDataクラスをjsonで保存するためにto_dict()という関数を用意し、dict形式のデータを取得できる様に実装を行う必要が出てきます。

   def to_dict(self):
        return {
            "version": self._version,
            "option_data": self._option_data.to_dict(),
            "game_data": self._game_data.to_dict(),
            "record_data": self._record_data.to_dict(),
        }

SaveDataのメンバにOptionDataやGameDataなどを持たせているので、それぞれにもto_dict()を用意する必要があります。
これでdictとしてデータが取得できる様になるのでjson.dump()でファイルに出力することができます。

ロード処理はjson.load()を使用してjsonファイルを読み込みます。
読み込んだdict形式のデータをSaveDataなどに書き戻していきます。

   @staticmethod
    def load_from_file(path):
        if not os.path.exists(path):
            return None
        with open(path, "r", encoding="utf-8") as f:
            d = json.load(f)
            return SaveData.from_dict(d)

SaveDataクラスにfrom_dict()を用意してdict形式のデータを渡して解析してデータに書き戻す処理を実装していきます。
to_dict()と同様にOptionDataクラスなどにもfrom_dict()を実装していく必要があります。

   @staticmethod
    def from_dict(d):
        s = SaveData()
        s._version = int(d.get("version", 1))
        s._option_data = OptionData.from_dict(d.get("option_data", {}))
        s._game_data = GameData.from_dict(d.get("game_data", {}))
        s._record_data = RecordData.from_dict(d.get("record_data", {}))
        return s

これでjsonファイルでセーブロードを行うことができる様になります。

開発中は平文のままでも良いですが、実際にセーブデータとして使用する場合には暗号化などの対応も必要となってくるのでご留意ください。

pickle

pickleというライブラリが用意されているのでこちらを利用してもセーブロードを行うことができます。

これはclassのインスタンスを渡すと勝手にシリアライズしてくれるようで、json形式の様にdict形式での出力や読み込みなどの実装は不要です。

セーブはpickle.dump()の関数を使用します。
引数でSaveDataのオブジェクト(インスタンス)を渡すとメンバすべてがシリアライズされてファイルに保存されます。

import pickle

def save_pickle(self):
    # フォルダが存在しない場合は作成する.
    save_folder = os.path.dirname(self._save_data_pickle_path)
    if not os.path.exists(save_folder):
        os.makedirs(save_folder)

    with open(self._save_data_pickle_path, 'wb') as file:
        pickle.dump(self._save_data, file)
    pass

ロード処理はpickle.load()を使用します。
読み込みが成功した場合はオブジェクトが返ってくるのでそのままSaveDataとして扱うことができます。

def load_pickle(self):
    if not os.path.exists(self._save_data_pickle_path):
        print("No pickle save file to load")
        return

    with open(self._save_data_pickle_path, 'rb') as file:
        loaded = pickle.load(file)
        if loaded is not None:
            self._save_data = loaded
            print(f"Pickle loaded: {self._save_data_pickle_path}")
            print(self._save_data)
        else:
            print("Failed to load pickle save data")
    pass

ブラウザ版

ブラウザ版を作成するにあたり上記の方法ではそのままでは保存できない様でした。
そこでブラウザのローカルストレージの機能を利用してセーブロードの機能を実装します。

Pyodideというブラウザ上でpythonを実行できる環境があり、js(JavaScript)を扱えるモジュールがあるので、localStorage.setImte()という関数でKeyの値と実際のデータを設定することでローカルストレージへの保存が行えます。
サンプルではjson形式に変換し、その値を保存する様にしています。

# `js` モジュールは Pyodide(ブラウザ)環境で提供される.
# ローカル実行(pyxel 実行環境)では存在しないため安全にフォールバックする.
try:
    from js import window
except Exception:
    window = None


def save_local_storage(self):
    if window is not None:
        try:
            data_str = json.dumps(self._save_data.to_dict())
            window.localStorage.setItem("pyxel_save_data", data_str)
            print("Saved to localStorage")
        except Exception as e:
            print(f"localStorage save error: {e}")
    else:
        print("localStorage not available in this environment")
    pass

ロード時はlocalStorage.GetItem()で保存したときのキーの値を指定することで保存されているデータを読み込むことができます。
読み込まれたデータをjson形式で読み込み直して、SaveDataに戻すようにしています。

def load_local_storage(self):
    if window is not None:
        try:
            data_str = window.localStorage.getItem("pyxel_save_data")
            if data_str is not None:
                data = json.loads(data_str)
                self._save_data = SaveData.from_dict(data)
                print("Loaded from localStorage")
            else:
                print("No localStorage save data to load")
        except Exception as e:
            print(f"localStorage load error: {e}")
    else:
        print("localStorage not available in this environment")
    pass

こちらも平文のままとなっているので、実際にセーブデータとして使用する場合には暗号化などの対応もご検討ください。

参考

github.com

note.com


【pyxel】オリジナルゲーム「オコシニツケタキビダンゴ」公開!

pyxelで作成したオリジナルゲーム「オコシニツケタキビダンゴ」のプロトタイプ版を公開しました!

ブラウザ版はこちらのリンクから遊ぶことができます。
スマホでも遊ぶことができるので、ぜひ遊んでみてください。

https://takezoh-1127.github.io/pyxel-public-momo/

概要

「むかしむかしあるところにおじいさんとおばあさんが暮らしていました...」

桃太郎のお供は犬、猿、キジだけではなかった!?
鬼ヶ島へ向かう道中ではいろいろなお供たちに出逢います。
おばあさんから渡されたキビダンゴは6つ。
限られたキビダンゴで仲間を集め、最強?最弱?バラエティに富んだパーティを組んで鬼ヶ島に挑め!

遊び方

  1. 桃太郎を選ぶ

  2. お供を仲間にする
    鬼ヶ島へ向かう道中で出会うお供を仲間にするためにはキビダンゴを渡す必要があります。
    お供ごとに必要なキビダンゴの数が決まっているので、必要なキビダンゴを持っていれば仲間にするか、しないかを選択することができます。
    仲間にするとキビダンゴが減っていきます。

  3. 鬼ヶ島を選ぶ
    選択した島で鬼の数、宝箱の数が異なります。

  4. バトルの順番を決定する

  5. 鬼とのバトル
    鬼とのバトルは1対1で行います。
    サイコロを振って数字の大きい方が攻撃を行います。
    ただし、サイコロの目が6と1の場合は1を出した側が攻撃となり、ダメージが2倍となります。
    また、同じ目の場合は引き分けとなります。
    攻撃が成功した場合は「ちから」がそのままダメージとなります。

  6. お宝を手に入れる
    バトルに勝利するとお宝を手に入れることができます。
    繰り返しプレイすることでどんどんお宝を増やしていきましょう!
    でも、あまり集めすぎると...

操作方法

PCであればゲームパッドが接続されていればゲームパッドでも操作できます。
キーボードで操作する場合はボタンのアサインは以下となります。

矢印キー カーソル移動
Enter 決定
ESC キャンセル

スマホの場合はバーチャルパッドが表示されるのでパッド操作で遊ぶことができます。

更新履歴

日付 内容
2026/2/15 ダメージ演出追加
2026/2/8 寂しかったのでタイトル画面にBGM追加
2026/2/6 プロトタイプ版公開

参考

github.com


【pyxel】pyxelで作ったゲームをネットで公開しよう!

概要

pyxelで作成したゲームをネットに公開する方法を紹介します!
今回はGithubを利用してネットに公開するための手順です。
Pythonのソースは公開しないでビルドしたバイナリで公開するための方法となります。

実際に公開しているテストプロジェクトは以下となります。
https://takezoh-1127.github.io/pyxel-public-neko/

作成したゲームをpyxappに変換する

今回公開したテストプロジェクトの元のプロジェクトは以下のGithubにアップしています。
こちらのリポジトリでpyxelによる実装を行なっています。

github.com

実装しているソースを管理している場所と、実行環境を公開しているリポジトリが異なる場所となっているため公開してみました。
こちらの環境でpyxappに変換します。

以下を実行してneko.pyxappをビルドします。

pyxel package ./ neko.py

appをhtmlに変換する

先ほど作成したneko.pyxappをhtmlに変換します。

pyxel app2html neko.pyxapp

neko.htmlが出力されるのでindex.htmlにファイル名を変更しておきます。
この状態でローカルで実行を確認しておきます。

Githubリポジトリを作成する

Githubの+ボタンからNew repositoryを選択します。

Repository nameに作成するリポジトリ名を入力します。
Add READMEも有効にしておきます。
今回はpyxel-public-nekoとしてリポジトリを作成します。

画面下のCreate repository で作成する。

ファイルのアップロード

作成したリポジトリにファイルをアップロードします。
先ほど作成したindex.htmlとappをアップロードします。

Commit changesを押します。
これでリポジトリにコミットされます。

index.htmlとnoko.pyxappが追加されていることが確認できます。

Github Pagesの設定

ブラウザで公開するための設定を行います。
上部のメニューのSettingsを選択し、左側の項目からPagesを選択します。

Branchでmainを選択してSaveボタンを押します。

Vist siteが表示されれば成功です。
反映までに多少時間がかかるケースがある様ですので、少し時間を空けてから再度ページにアクセスしてください。

ゲームの起動を確認

Vist siteを押すことでブラウザで起動が確認できます。

iPhoneのブラウザからも起動が確認できました。

これで無事に公開することができました。
こちらのURLを教えて色々な人に触ってもらえる様になります。

更新方法

再度pyxappのビルド、html出力を行い、index.htmlと.pyxappをアップロードすることで更新されます。
githubで反映されるのに時間がかかるケースがあるので少し時間を空けてからアクセスしてください。
ブラウザによってはキャッシュで更新が反映されない場合があるので強制リロードを試してください。

参考

github.com


【UE5】DeveloperSettingsを使ってみよう!(C++編)

概要

UEの「プロジェクト設定」や「エディタ設定」にプロジェクトの独自のパラメータを追加する方法についてです。

ゲームの初期値の設定や、デバッグ時などの設定などをプロジェクト設定で行うことができるようになります。

たとえば

  • プロジェクト独自のグラフィクスの設定をプロジェクト設定で行えるようにする
  • アセットの読み込みを行う際などにC++のソースにパスをハードコーディングせずに、設定したアセットのパスで読み込みを行うようにする
  • ゲーム起動のモードなどをプロジェクト設定で行えるようにする

などの用途が考えられます。

また、設定した値はConfig/DefaultGame.iniなどのiniファイルに保存されます。

動作環境

UnrealEngine 5.4.4

実装

UDeveloperSettingsを継承したクラスを作成し、設定を行いたい項目をUPROPERTYマクロを使用して変数を宣言することで、エディタの「プロジェクト設定」などに設定項目が追加されます。
作成されたヘッダーファイルに以下の項目を追加します。

UCLASS(config = Game, defaultconfig)
class TESTPROJECT_API UHogeDeveloperSettings : public UDeveloperSettings
{
    GENERATED_BODY()
    
public:
    UPROPERTY(EditAnywhere, Config)
    bool SettingBool;

    UPROPERTY(EditAnywhere, Config)
    int SettingInt;

    UPROPERTY(EditAnywhere, Config)
    float SettingFloat;

    UPROPERTY(EditAnywhere, Config)
    FVector SettingVector;

    UPROPERTY(EditAnywhere, Config)
    FString SettingString;

    UPROPERTY(EditAnywhere, Config)
    TSoftObjectPtr<UDataTable> SettingDataTable;
};

クラスの定義でUCLASS(config=Game, defaultconfig)と定義しているので、プロジェクト設定のゲームのカテゴリに追加の指定となります。
defaultconfigとしているのでDefaultGame.iniに保存する指定となります。
また、変数(項目)のUPROPERTのConfigの指定でiniファイルへの保存の指定となります。
ビルド後にエディタを起動し、プロジェクト設定に追加した項目が反映されていることが確認できます。
表示項目はクラス名がそのまま設定されています。

設定を変更するとDefautlGame.iniに以下の項目が保存されていることが確認できます。

[/Script/TestProject.HogeDeveloperSettings]
SettingBool=True

configの設定をEngine、Editorなどと指定することでカテゴリの指定、保存するiniファイルを設定できます。

UCLASSでDisplayNameを使用することで表示名を指定することができます。

UCLASS(config = Game, defaultconfig, meta = (DisplayName = "テスト設定"))
class TESTPROJECT_API UHogeDeveloperSettings : public UDeveloperSettings
{
    GENERATED_BODY()

UDeveloperSettingsの以下の関数を使用することで、カテゴリやセクション名なども設定できます。

UCLASS(config = Game, defaultconfig, meta = (DisplayName = "テスト設定"))
class TESTPROJECT_API UHogeDeveloperSettings : public UDeveloperSettings
{
    GENERATED_BODY()
    
public:
    /** Gets the settings container name for the settings, either Project or Editor */
    virtual FName GetContainerName() const override;
    /** Gets the category for the settings, some high level grouping like, Editor, Engine, Game...etc. */
    virtual FName GetCategoryName() const override;
    /** The unique name for your section of settings, uses the class's FName. */
    virtual FName GetSectionName() const override;

#if WITH_EDITOR
    /** Gets the section text, uses the classes DisplayName by default. */
    virtual FText GetSectionText() const override;
    /** Gets the description for the section, uses the classes ToolTip by default. */
    virtual FText GetSectionDescription() const override;
#endif

GetCategoryName()、GetSectionText()、GetSectionDescription()の設定が表示名に反映されます。

#define LOCTEXT_NAMESPACE "HogeDeveloperSettings"

FName UHogeDeveloperSettings::GetContainerName() const
{
    return Super::GetContainerName();
}
FName UHogeDeveloperSettings::GetCategoryName() const
{
    FText CategoryText = LOCTEXT("TestCategory", "テストカテゴリ");

    return FName(*CategoryText.ToString());
}
FName UHogeDeveloperSettings::GetSectionName() const
{
    return Super::GetSectionName();
}

#if WITH_EDITOR
FText UHogeDeveloperSettings::GetSectionText() const
{
    return LOCTEXT("TestSection", "テストセクション");
}
FText UHogeDeveloperSettings::GetSectionDescription() const
{
    return LOCTEXT("TestSectionDesc", "テストセクション説明");
}
#endif

#undef LOCTEXT_NAMESPACE

カテゴリを指定することでゲームやエンジン、エディタなどと同じ階層に追加されるようになります。
カテゴリの中がセクション名が適用されて表示されます。

参照方法

設定された値にC++でアクセスするには以下の静的関数を使用します。

 const UHogeDeveloperSettings* Settings = GetDefault<UHogeDeveloperSettings>();

    if (Settings)
    {
        UE_LOG(LogTemp, Log, TEXT("HogeDeveloperSettings:"));
        UE_LOG(LogTemp, Log, TEXT("  SettingBool: %s"), Settings->SettingBool ? TEXT("true") : TEXT("false"));
        UE_LOG(LogTemp, Log, TEXT("  SettingInt: %d"), Settings->SettingInt);
        UE_LOG(LogTemp, Log, TEXT("  SettingFloat: %f"), Settings->SettingFloat);
        UE_LOG(LogTemp, Log, TEXT("  SettingVector: %s"), *Settings->SettingVector.ToString());
        UE_LOG(LogTemp, Log, TEXT("  SettingString: %s"), *Settings->SettingString);
        if (Settings->SettingDataTable.IsValid())
        {
            UE_LOG(LogTemp, Log, TEXT("  SettingDataTable: %s"), *Settings->SettingDataTable->GetName());
        }
        else
        {
            UE_LOG(LogTemp, Log, TEXT("  SettingDataTable: nullptr"));
        }
    }

値を設定して実行時に参照してみます。

以下の様なログで反映されていることが確認できます。

[2025.12.16-01.58.45:177][231]LogTemp: HogeDeveloperSettings:
[2025.12.16-01.58.45:559][231]LogTemp:   SettingBool: true
[2025.12.16-01.58.45:853][231]LogTemp:   SettingInt: 10
[2025.12.16-01.58.46:244][231]LogTemp:   SettingFloat: 100.500000
[2025.12.16-01.58.46:542][231]LogTemp:   SettingVector: X=10.000 Y=20.000 Z=30.000
[2025.12.16-01.58.46:894][231]LogTemp:   SettingString: TEST
[2025.12.16-01.58.49:823][231]LogTemp:   SettingDataTable: nullptr

試しにWin64版のパッケージを作成して実行したところ、同様に設定が反映されていることが確認できます。

参考

historia.co.jp


【Jenkins】PipelineのスクリプトをGitHubで管理してみよう!

概要

JenkinsでGitHubを使用するためにはGitHub側と連携する必要があります。

今回はJenkinsのジョブのパイプラインでスクリプトのファイルをバージョン管理を行いたいと思います。
Pipeline script from SCMの場合、jenkinsfileなどをGitHubで管理するための方法についてです。

試しにjenkinsfileのファイルに以下のコードを記述して、GitHubにコミットしたリポジトリを用意しています。

#!groovy

// pipelineのテスト.
pipeline {
    agent any
    stages {
        stage('checkout') {
            steps {
                echo 'start stage checkout.'
            }
        }
        
        stage('build') {
            steps {
                echo 'start stage build.'
            }
        }
    }
}

GitのインストールやJenkinsのインストールについてはこちらを参考にしていただければと思います。

www.main-function.com

www.main-function.com

GitHubのPersonal Access Tokenの作成

Jenkins側からGitHubのプライベートリポジトリにアクセスできるようにするために、GitHubのPersonal Access Tokenを作成する必要があります。

アカウントの"Settings" → "Developer settings" → "Personal access tokens" の "Tokens(classic)" を選択します。

・Settingsの画面

・Developer settingsの画面

Tokens(classic)の画面で "Generate new token" を選択します。

今回はプライベートリポジトリへのアクセスが必要なので "repo" のみにチェックを入れています。
ページの下にある "Generate token" を押してトークンを作成します。

トークンが作成されたのでコピーしておきましょう。

Jenkinsの設定

Gitの設定の確認

Gitのパスの設定を確認します。
"Jenkinsの管理" → System Configurationの "Tools" を選択します。
インストール済みGitの項目のGit実行形式へのパスの項目でエラーが出ていないことを確認します。
設定されていない場合はインストールしたgitの実行ファイルのパスを設定します。

認証情報の追加

Jenkinsの認証情報の追加を行います。
"Jenkinsの管理" → "認証情報" → ”System" → "グローバルドメイン" の Add Credentials ボタンを押します。

種類で "ユーザー名とパスワード" を選択します。
ユーザー名にgithubのユーザー名を指定します。
パスワードに先ほどgithubで作成したトークンを指定します。
IDは任意の名前を設定します。
これはJenkins側でユニークなID(名前)にしておく必要があります。

ジョブのパイプラインの設定

パイプラインの項目の定義を "Pipeline script from SCM" を選択します。
SCMで "Git" を選択します。
リポジトリの項目でリポジトリのURLと認証情報、ブランチ指定を設定します。
認証情報で先ほど作成した認証情報を指定します。
ブランチ指定はmasterではなくmainとしているので、ここも変更する必要があります。

実行してgithubから取得されて、jenkinsfileで記述されたスクリプトの処理が成功していることが確認できます。

GitHubからの取得先はjenkinsのワークスペースに取得されているようです。
今回は以下のパスに取得されていることが確認できました。

C:\ProgramData\Jenkins.jenkins\workspace\test-pipeline02

Shared Libraries

複数のpipelineのジョブで使用する共通処理を管理する方法です。

フォルダ構成としては以下の構成にする必要があります。

www.jenkins.io

root/
    +vars
    +resource
    Jenkinsfile

vars以下に共通処理のgroovyファイルを置きます。
vars以下に置いたファイル名がそのままグローバルの変数としてアクセスできるようになります。

Global Librariesの使用方法

Jenkinsの管理 → System → Global Trusted Pipeline Librariesの項目を以下の内容で設定します。

NameをMyTestLibraryとしておきます。
これはjenkinsfileなどのgroovyで@Library()で参照する際の名前となります。
Default versionはGitHubのbranchのmainを指定しておきます。
SCMはGitを選択します。(GitHubもありますが、今回はGitを選択します)
リポジトリと認証情報も設定します。
それ以外はデフォルトのままとします 。

vars以下にmy_test.groovyというファイルを用意します。
このファイルにcall関数を追加すると 'ファイル名()' で呼び出すことができます。

#!groovy

def call( Map params = [:] ) {
    echo "call my_test()"
}

Jenkinsfileの方で関数の呼び出しを行ってみます。

#!groovy

// Global Trusted Pipeline Librariesで指定したNameを指定.
// _ですべてを対象する.
@Library( 'MyTestLibrary' ) _

pipeline {
    agent any
    stages {
        stage('checkout') {
            steps {
                echo 'start stage checkout.'
            }
        }
        
        stage('build') {
            steps {
                echo 'start stage build.'

                // Shared Libiraryの呼び出しのテスト.
                // my_test.groovyのファイル名が関数として呼び出せる.
                my_test()
            }
        }
    }
}

実行するとmy_testが呼び出されていることが確認できます。