目次
HspppLib とは
HSPPP (Hot Soup Processor Plus Plus) は、HSP (Hot Soup Processor) 互換の C++23 ライブラリです。
HSPで培った知識とコーディングスタイルを活かしながら、C++の型安全性、パフォーマンス、拡張性を手に入れることができます。
GitHub: Velgail/HSPPP_Lib
特徴
| 機能 | 説明 |
|---|---|
| 🎮 HSP互換API | screen, color, boxf, mes などお馴染みの命令をそのまま使用可能 |
| 📦 モダンC++ | C++23 の機能を活用した型安全・メモリ安全な設計 |
| 🔧 デュアルスタイル | HSP風のグローバル関数とOOP風のメソッドチェーン、お好みで選択 |
| 🖼️ Direct2D描画 | 高品質なハードウェアアクセラレーション描画 |
| ⚡ ゼロオーバーヘッド | C++の哲学「使わないものにコストを払わない」 |
| 🛡️ 安全性重視 | 生ポインタ禁止、RAII、例外によるエラー処理 |
なぜHspppなのか
HSPは素晴らしい言語ですが、以下のような限界があります:
- 大規模プロジェクトでの保守性
- 型チェックの欠如によるバグ
- 外部ライブラリとの連携の難しさ
- パフォーマンスの上限
HspppはこれらをC++の力で解決しつつ、HSPの「書きやすさ」を維持します。
設計思想: Pragmatic Hybrid
Hspppの設計思想は 「Pragmatic Hybrid(実用主義的ハイブリッド)」 です。
設計目標
- HSP再現性:
hspMain内ではHSPとほぼ同じロジックで記述できる - 安全性: C++の型システムとRAII(自動リソース管理)により、メモリリークや型エラーを防ぐ
- 拡張性: ユーザーはいつでも「HSPの皮」を破り、DirectXやWin32 APIの深層へアクセスできる
ターゲットユーザー
- HSPでの開発に限界を感じ、C++へ移行したい
- しかし、Javaのような過度な儀式(ボイラープレート)は嫌う
- 「手軽さ」と「パワー」の両立を求める
環境要件とセットアップ
必須環境
| 項目 | 要件 |
|---|---|
| OS | Windows 11 (64-bit) |
| コンパイラ | Visual Studio 2026 (VS 18) |
| C++標準 | C++23 (/std:c++latest) |
| 必須ライブラリ | Direct2D, DirectWrite (Windows SDK に含まれる) |
⚠️ 重要: Visual Studio 2022 (VS 17) ではビルドできません。必ずVS 2026を使用してください。
ビルド手順
Visual Studio から
- HspppLib.slnx を Visual Studio 2026 で開く
- プラットフォームを x64 に設定
- ビルド (
F7またはCtrl+Shift+B)
コマンドライン (MSBuild) から
# 文字化け防止(必須)
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8
# Debug ビルド
& "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" `
HspppLib.slnx /p:Configuration=Debug /p:Platform=x64 /m
# Release ビルド
& "C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe" `
HspppLib.slnx /p:Configuration=Release /p:Platform=x64 /m
実行
& "x64\Debug\HspppSample.exe"
プロジェクト構成
HspppLib/
├── HspppLib/ # ライブラリ本体(静的ライブラリ)
│ ├── module/ # C++23 モジュール (.ixx)
│ │ ├── hsppp.ixx # メインモジュール(これだけimportすればOK)
│ │ ├── hsppp_types.ixx # 型定義(OptInt, Screen, Quad等)
│ │ ├── hsppp_screen.ixx # 画面制御(screen, buffer, gsel等)
│ │ ├── hsppp_drawing.ixx # 描画命令(mes, boxf, line等)
│ │ ├── hsppp_input.ixx # 入力処理(stick, getkey, mouse等)
│ │ ├── hsppp_math.ixx # 数学関数(sin, cos, rnd等)
│ │ ├── hsppp_string.ixx # 文字列操作(strmid, instr, NotePad等)
│ │ ├── hsppp_file.ixx # ファイル/GUI(exist, bload, button等)
│ │ ├── hsppp_interrupt.ixx # 割り込み(onclick, onerror等)
│ │ └── hsppp_media.ixx # メディア(mmplay, mmstop等)
│ └── src/
│ ├── boot/ # エントリポイント(WinMain)
│ └── core/ # 内部実装
│
├── HspppSample/ # サンプルアプリケーション(.exe)
│ ├── UserApp.cpp # ユーザーコード(hspMainを記述)
│ ├── DemoDrawBasic.cpp # 基本描画デモ
│ ├── DemoDrawExtended.cpp # 拡張描画デモ
│ ├── DemoDrawGUI.cpp # GUIデモ
│ └── ...
│
├── HspppTest/ # 単体テスト
└── docs/ # ドキュメント(GitHub Pages用)
はじめてのHsppp
最小限のプログラム
// UserApp.cpp
import hsppp;
using namespace hsppp;
// エントリポイント: hspMain() を定義(main/WinMain は書かない)
void hspMain() {
// ウィンドウ作成
screen(0, 640, 480);
title("Hello HSPPP!");
// 描画
color(255, 0, 0); // 赤色を設定
boxf(100, 100, 200, 200); // 矩形を塗りつぶし
color(255, 255, 255); // 白色を設定
pos(120, 140); // 描画位置を設定
mes("Hello, HSPPP!"); // 文字列を描画
// hspMain を抜けると stop() と同等(ウィンドウは閉じずに待機)
return;
}
HSPとの対比
| HSP | HSPPP |
|---|---|
| (自動実行) | void hspMain() { ... } |
screen 0, 640, 480 |
screen(0, 640, 480); |
title "Hello" |
title("Hello"); |
color 255, 0, 0 |
color(255, 0, 0); |
boxf 100, 100, 200, 200 |
boxf(100, 100, 200, 200); |
stop |
stop(); |
重要な違い
- main/WinMain は書かない: ライブラリ側が提供
- 行末にセミコロン
;: C++の文法 - 命令は関数呼び出し: カッコ
()で引数を囲む - 文字列はダブルクォート:
"..."のみ(シングルクォート'...'は文字1つ) - 変数は型を宣言:
int x = 0;のように書く
HSP互換スタイル vs OOPスタイル
Hspppは2つのコーディングスタイルをサポートします。
HSP互換スタイル(グローバル関数)
import hsppp;
using namespace hsppp;
void hspMain() {
// HSPとほぼ同じ書き方
screen(0, 640, 480);
color(255, 0, 0);
boxf(100, 100, 200, 200);
color(255, 255, 255);
pos(120, 140);
mes("Hello!");
return;
}
OOPスタイル(メソッドチェーン)
import hsppp;
using namespace hsppp;
void hspMain() {
// 構造体による初期化(Designated Initializers)
auto win = screen({.width = 640, .height = 480, .title = "OOP Style"});
// メソッドチェーンで連続描画
win.color(255, 0, 0)
.boxf(100, 100, 200, 200)
.color(255, 255, 255)
.pos(120, 140)
.mes("Method Chaining!");
return;
}
どちらを使うべきか?
| スタイル | 適したケース |
|---|---|
| HSP互換 | HSPからの移行初期、シンプルなプログラム |
| OOP | 複数ウィンドウの管理、大規模プロジェクト |
両方を混在させることも可能です。
パラメータ省略の仕組み
HSPでは screen , 800, 600 のように任意の位置でパラメータを省略できます。Hspppでもこれを実現しています。
省略の方法
// 方法1: omit キーワード
screen(omit, 800, 600); // ID=0(省略), 800x600
screen(1, omit, omit, screen_hide); // サイズ省略、非表示モード
// 方法2: 空の波括弧 {}
screen({}, 800, 600); // ID=0(省略), 800x600
なぜ omit なのか
C++26 では _(アンダースコア)が「名前独立宣言」として特別な意味を持つようになります。将来の互換性のため、omit キーワードを採用しています。
内部の仕組み: OptInt / OptDouble
// 省略可能なint型
struct OptInt {
std::optional<int> value_;
constexpr OptInt() noexcept : value_(std::nullopt) {} // 省略
constexpr OptInt(int v) noexcept : value_(v) {} // 値指定
bool is_default() const noexcept { return !value_.has_value(); }
int value_or(int def) const noexcept { return value_.value_or(def); }
};
型システムの理解
基本型の対応
| HSP | HSPPP (C++) | 説明 |
|---|---|---|
| 整数 | int |
32bit整数 |
| 実数 | double |
64bit浮動小数点 |
| 文字列 | std::string |
UTF-8文字列 |
| 配列 | std::vector<T> |
動的配列 |
Hsppp独自の型
OptInt / OptDouble / OptInt64
省略可能なパラメータを表現
void screen(int id, OptInt width = {}, OptInt height = {});
Screen
ウィンドウへの軽量ハンドル。内部ではIDのみを保持。
Screen win = screen(0, 640, 480);
win.color(255, 0, 0).boxf(0, 0, 100, 100);
Cel
画像素材(スプライト)へのハンドル
Cel sprite = loadCel("player.png");
sprite.divide(4, 4); // 4x4分割
sprite.put(0, 100, 100); // セル0を描画
Quad / QuadUV / QuadColors
gsquare用の4頂点座標
Quad dst(0, 0, 100, 0, 100, 100, 0, 100); // 左上, 右上, 右下, 左下
gsquare(-1, dst); // 単色塗りつぶし
DialogResult
dialog命令の戻り値(intとstringの両方に変換可能)
DialogResult result = dialog("OK?", dialog_yesno);
if (result) { // int への変換(stat相当)
std::string path = result; // string への変換(refstr相当)
}
NotePad
OOP版メモリノートパッド
NotePad note;
note.load("data.txt");
std::string line = note.get(0); // 1行目を取得
note.add("新しい行").save("data.txt");
HspError / HspErrorRecoverable
例外クラス(onerrorで捕捉可能)
API リファレンス
画面制御
screen / buffer / bgscr
// HSP互換
Screen screen(int id, OptInt width = {}, OptInt height = {}, OptInt mode = {}, ...);
Screen buffer(int id, OptInt width = {}, OptInt height = {}, OptInt mode = {});
Screen bgscr(int id, OptInt width = {}, OptInt height = {}, OptInt mode = {}, ...);
// OOP(ID自動採番)
Screen screen(const ScreenParams& params);
Screen screen(); // デフォルト設定
// パラメータ構造体
struct ScreenParams {
int width = 640;
int height = 480;
int mode = 0;
int pos_x = -1;
int pos_y = -1;
std::string_view title = "HSPPP Window";
};
modeフラグ:
screen_normal (0): フルカラーモードscreen_hide (2): 非表示screen_fixedsize (4): サイズ固定screen_tool (8): ツールウィンドウscreen_fullscreen (256): フルスクリーン(bgscr用)
gsel
void gsel(OptInt id = {}, OptInt mode = {});
// mode: -1=非表示, 0=影響なし, 1=アクティブ, 2=アクティブ+最前面
gmode
void gmode(OptInt mode = {}, OptInt size_x = {}, OptInt size_y = {}, OptInt blend_rate = {});
// mode: 0=通常, 1=透明色, 2=半透明, 3=透明色+半透明, 4=加算, 5=減算, 6=乗算
// blend_rate: 0~256(256で不透明)
gcopy / gzoom / grotate
void gcopy(OptInt src_id = {}, OptInt src_x = {}, OptInt src_y = {}, OptInt size_x = {}, OptInt size_y = {});
void gzoom(OptInt dest_w = {}, OptInt dest_h = {}, OptInt src_id = {}, OptInt src_x = {}, OptInt src_y = {}, OptInt src_w = {}, OptInt src_h = {}, OptInt mode = {});
void grotate(OptInt srcId = {}, OptInt srcX = {}, OptInt srcY = {}, OptDouble angle = {}, OptInt dstW = {}, OptInt dstH = {});
redraw / await / cls
void redraw(int p1 = 1); // 0=描画予約, 1=画面反映
void await(int time_ms); // 待機(ミリ秒)
void cls(OptInt p1 = {}); // 0=白, 1=明灰, 2=灰, 3=暗灰, 4=黒
picload / bmpsave / celload
void picload(std::string_view filename, OptInt mode = {});
void bmpsave(std::string_view filename);
int celload(std::string_view filename, OptInt id = {});
void celdiv(int id, int divX, int divY);
void celput(int id, int cellIndex, OptInt x = {}, OptInt y = {});
// OOP版
Cel loadCel(std::string_view filename);
描画命令
color / pos / mes
void color(int r, int g, int b);
void pos(int x, int y);
void mes(std::string_view text, OptInt sw = {});
// sw: 1=改行しない, 2=影, 4=縁取り, 8=簡易描画, 16=gmode設定
void print(std::string_view text, OptInt sw = {}); // mesの別名
boxf / line / circle / pset / pget
void boxf(int x1, int y1, int x2, int y2);
void boxf(); // 画面全体
void line(OptInt x2 = {}, OptInt y2 = {}, OptInt x1 = {}, OptInt y1 = {});
void circle(OptInt x1 = {}, OptInt y1 = {}, OptInt x2 = {}, OptInt y2 = {}, OptInt fillMode = {});
// fillMode: 0=線, 1=塗りつぶし
void pset(OptInt x = {}, OptInt y = {});
void pget(OptInt x = {}, OptInt y = {}); // 色を取得して選択色に設定
gradf / grect / gsquare
void gradf(OptInt x = {}, OptInt y = {}, OptInt w = {}, OptInt h = {}, OptInt mode = {}, OptInt color1 = {}, OptInt color2 = {});
// mode: 0=上→下, 1=左→右, 2=左上→右下
void grect(OptInt cx = {}, OptInt cy = {}, OptDouble angle = {}, OptInt w = {}, OptInt h = {});
void gsquare(int srcId, const Quad& dst); // 単色
void gsquare(int srcId, const Quad& dst, const QuadUV& src); // 画像
void gsquare(int srcId, const Quad& dst, const QuadColors& colors); // グラデーション
font / sysfont
int font(std::string_view fontName, OptInt size = {}, OptInt style = {}, OptInt decorationWidth = {});
// style: 0=通常, 1=太字, 2=斜体, 3=太字+斜体
void sysfont(OptInt type = {});
// type: 0=HSP標準, 17=デフォルトGUI
hsvcolor / rgbcolor / syscolor
void hsvcolor(int h, int s, int v); // HSV形式(h: 0-191, s: 0-255, v: 0-255)
void rgbcolor(int rgb); // 0xRRGGBB形式
void syscolor(int id); // システムカラー
入力処理
stick
int stick(OptInt nonTrigger = {}, OptInt checkActive = {});
ビットマスク定数:
stick_left = 1; // ←
stick_up = 2; // ↑
stick_right = 4; // →
stick_down = 8; // ↓
stick_space = 16; // Space
stick_enter = 32; // Enter
stick_ctrl = 64; // Ctrl
stick_esc = 128; // ESC
stick_lbutton = 256; // マウス左
stick_rbutton = 512; // マウス右
stick_tab = 1024; // Tab
stick_z = 2048; // Z
stick_x = 4096; // X
stick_c = 8192; // C
stick_a = 16384; // A
stick_w = 32768; // W
stick_d = 65536; // D
stick_s = 131072; // S
使用例:
int key = stick(15); // 方向キーは押しっぱなしでも検出
if (key & stick_left) x -= 5;
if (key & stick_right) x += 5;
if (key & stick_space) fire();
getkey
int getkey(int keycode); // 押されていれば1
mouse / mousex / mousey / mousew
void mouse(OptInt x = {}, OptInt y = {}, OptInt mode = {});
// mode: 0=負値で非表示, -1=移動+非表示, 1=移動のみ, 2=移動+表示
int mousex(); // X座標
int mousey(); // Y座標
int mousew(); // ホイール移動量
数学関数
三角関数(ラジアン)
// <cmath>の関数をそのまま再エクスポート
using std::sin, std::cos, std::tan;
using std::asin, std::acos, std::atan, std::atan2;
using std::sinh, std::cosh, std::tanh;
角度変換
double deg2rad(double degrees); // 度→ラジアン
double rad2deg(double radians); // ラジアン→度
乱数
int rnd(int max); // 0〜max-1の乱数(HSP互換のLCG)
void randomize(OptInt seed = {}); // シード設定(省略時は時刻ベース)
範囲制限
int limit(int value, OptInt min = {}, OptInt max = {});
double limitf(double value, OptDouble min = {}, OptDouble max = {});
イージング
void setease(double start, double end, OptInt type = {});
int getease(int progress, OptInt max = {});
double geteasef(double progress, OptDouble max = {});
イージングタイプ:
ease_linear = 0;
ease_quad_in = 1;
ease_quad_out = 2;
ease_quad_inout = 3;
ease_cubic_in = 4;
// ... (bounce, shake等もあり)
ease_loop = 0x100; // ループフラグ
ソート
void sortval(std::vector<int>& arr, OptInt order = {});
void sortval(std::vector<double>& arr, OptInt order = {});
void sortstr(std::vector<std::string>& arr, OptInt order = {});
void sortnote(std::string& note, OptInt order = {});
int sortget(int index); // ソート前のインデックスを取得
数学定数
M_PI; // π
M_PI_2; // π/2
M_PI_4; // π/4
M_E; // e(自然対数の底)
M_LOG2E; // log₂(e)
M_LN2; // ln(2)
// ...
文字列操作
基本操作
int64_t strlen(const std::string& s); // 長さ
std::string strmid(const std::string& s, int64_t start, int64_t length); // 部分文字列
int64_t instr(const std::string& s, int64_t start, const std::string& search); // 検索
int64_t strrep(std::string& s, const std::string& search, const std::string& replace); // 置換
std::string strtrim(const std::string& s, int mode = 0, int char_code = 32); // トリム
std::vector<std::string> split(const std::string& s, const std::string& delimiter); // 分割
型変換
int toInt(double value);
int toInt(const std::string& s);
double toDouble(int value);
double toDouble(const std::string& s);
std::string str(int value);
std::string str(double value);
書式付き文字列
std::string strf(const std::string& format, ...);
// HSP互換のprintf風書式
// %d: 整数, %f: 実数, %s: 文字列, %x: 16進数
パス操作
std::string getpath(const std::string& path, int type);
// type: 0=フルパス, 1=ディレクトリ名なし, 2=拡張子なし, 8=小文字変換, 16=ディレクトリのみ, 32=拡張子のみ
文字コード変換
std::u16string cnvstow(const std::string& utf8); // UTF-8 → UTF-16
std::string cnvwtos(const std::u16string& utf16); // UTF-16 → UTF-8
std::string cnvstoa(const std::string& utf8); // UTF-8 → ShiftJIS
std::string cnvatos(const std::string& sjis); // ShiftJIS → UTF-8
NotePad クラス(OOP版メモリノートパッド)
class NotePad {
size_t count() const; // 行数
size_t size() const; // バイト数
std::string get(size_t index) const; // 行取得
NotePad& add(std::string_view text, int index = -1, int overwrite = 0); // 追加
NotePad& del(size_t index); // 削除
NotePad& clear(); // クリア
int find(std::string_view search, int mode = 0, size_t start = 0) const; // 検索
NotePad& load(std::string_view filename, size_t maxSize = 0); // ロード
bool save(std::string_view filename) const; // セーブ
};
メモリノートパッド命令(HSP互換)
void notesel(std::string& buffer); // バッファ選択
void noteunsel(); // バッファ解除
void noteadd(std::string_view text, OptInt index = {}, OptInt overwrite = {});
void notedel(int index);
void noteget(std::string& dest, OptInt index = {});
void noteload(std::string_view filename, OptInt maxSize = {});
void notesave(std::string_view filename);
int notefind(std::string_view search, OptInt mode = {});
int noteinfo(int type); // type: notemax(0)=行数, notesize(1)=バイト数
ファイル操作
ファイル操作
int64_t exist(const std::string& filename); // サイズ取得(-1=存在しない)
void chdir(const std::string& dirname); // ディレクトリ移動
void mkdir(const std::string& dirname); // ディレクトリ作成
void deletefile(const std::string& filename); // ファイル削除
void bcopy(const std::string& src, const std::string& dest); // コピー
int64_t bload(const std::string& filename, std::string& buffer, OptInt64 size = {}, OptInt64 offset = {});
int64_t bload(const std::string& filename, std::vector<uint8_t>& buffer, OptInt64 size = {}, OptInt64 offset = {});
int64_t bsave(const std::string& filename, const std::string& buffer, OptInt64 size = {}, OptInt64 offset = {});
int64_t bsave(const std::string& filename, const std::vector<uint8_t>& buffer, OptInt64 size = {}, OptInt64 offset = {});
std::vector<std::string> dirlist(const std::string& filemask, OptInt mode = {});
ディレクトリ情報
std::string dirinfo(int type);
// type: 0=カレント, 1=exe, 2=Windows, 3=System, 4=コマンドライン
// 便利関数
std::string dir_cur(); // カレントディレクトリ
std::string dir_exe(); // 実行ファイルのディレクトリ
std::string dir_win(); // Windowsディレクトリ
std::string dir_sys(); // Systemディレクトリ
std::string dir_cmdline(); // コマンドライン
std::string dir_desktop(); // デスクトップ
std::string dir_mydoc(); // マイドキュメント
exec
int exec(const std::string& filename, OptInt mode = {}, const std::string& command = "");
// mode: exec_normal(0), exec_minimized(2), exec_shellexec(16), exec_print(32)
dialog
DialogResult dialog(const std::string& message, OptInt type = {}, const std::string& option = "");
// type: dialog_info(0), dialog_warning(1), dialog_yesno(2), dialog_open(16), dialog_save(17), dialog_color(32)
GUIオブジェクト
オブジェクト設定
void objsize(OptInt sizeX = {}, OptInt sizeY = {}, OptInt spaceY = {});
void objmode(OptInt mode = {}, OptInt tabMove = {});
// mode: objmode_normal(0), objmode_guifont(1), objmode_usefont(2), objmode_usecolor(4)
void objcolor(OptInt r = {}, OptInt g = {}, OptInt b = {});
button
// ラムダ式でコールバックを指定
int button(std::string_view name, std::function<void()> callback);
// 使用例
button("Click Me", []() {
mes("Clicked!");
});
input / mesbox
// ⚠️ 必ず shared_ptr を使用(ライフタイム安全性のため)
int input(std::shared_ptr<std::string> var, OptInt sizeX = {}, OptInt sizeY = {}, OptInt maxLen = {});
int mesbox(std::shared_ptr<std::string> var, OptInt sizeX = {}, OptInt sizeY = {}, OptInt style = {}, OptInt maxLen = {});
// 使用例
auto text = std::make_shared<std::string>("初期値");
input(text, 200, 24);
// 後で値を取得
mes(*text);
chkbox / combox / listbox
// ⚠️ 必ず shared_ptr を使用
int chkbox(std::string_view label, std::shared_ptr<int> var);
int combox(std::shared_ptr<int> var, OptInt expandY, std::string_view items);
int listbox(std::shared_ptr<int> var, OptInt expandY, std::string_view items);
// 使用例
auto selected = std::make_shared<int>(0);
combox(selected, 100, "Option A\nOption B\nOption C");
// 後で値を取得
mes(strf("Selected: %d", *selected));
オブジェクト操作
void clrobj(OptInt startId = {}, OptInt endId = {}); // オブジェクト削除
void objprm(int objectId, std::string_view value); // 内容変更(文字列)
void objprm(int objectId, int value); // 内容変更(整数)
void objenable(int objectId, OptInt enable = {}); // 有効/無効
void objsel(int objectId); // フォーカス設定
割り込み・エラー処理
割り込みハンドラ
// グローバル版
void onclick(InterruptHandler handler);
void onkey(InterruptHandler handler);
void oncmd(InterruptHandler handler, int messageId);
// OOP版(Screenごと)
win.onclick([](){ /* クリック処理 */ });
win.onkey([](){ /* キー処理 */ });
// InterruptHandler = std::function<void()>
stop
void stop(); // 割り込み待機(hspMainを抜けても同等)
onerror
void onerror(ErrorHandler handler);
void onerror(); // 解除
// ErrorHandler = std::function<void(const HspError&)>
// 戻り値: 0=継続, 非0=終了
// 使用例
onerror([](const HspError& e) {
mes(strf("Error %d: %s", e.error_code(), e.message().c_str()));
mes(strf("at %s:%d", e.file_name().c_str(), e.line_number()));
});
HspError クラス
class HspError : public std::runtime_error {
int error_code() const;
int line_number() const;
const std::string& file_name() const;
const std::string& function_name() const;
const std::string& message() const;
};
エラーコード定数
ERR_NONE = 0;
ERR_SYNTAX = 1;
ERR_ILLEGAL_FUNCTION = 2;
ERR_OUT_OF_MEMORY = 4;
ERR_TYPE_MISMATCH = 5;
ERR_OUT_OF_ARRAY = 6;
ERR_OUT_OF_RANGE = 7;
ERR_DIVIDE_BY_ZERO = 8;
ERR_FILE_IO = 12;
ERR_WINDOW_INIT = 13;
// ...
メディア
void mmload(std::string_view filename, int id, OptInt flag = {});
void mmplay(int id, OptInt flag = {});
void mmstop(OptInt id = {});
void mmvol(int id, int volume); // 音量 (0-1000)
void mmpan(int id, int pan); // パン (-1000 to 1000)
HSPからの移行ガイド
基本的な違い
| 項目 | HSP | HSPPP (C++) |
|---|---|---|
| エントリポイント | なし(自動実行) | void hspMain() |
| 変数宣言 | 不要 | 必須 int x = 0; |
| 行末 | 改行 | セミコロン ; |
| 文字列 | "..." or '...' |
"..." のみ |
| コメント | // or ; |
// or /* */ |
| ラベル | *label |
関数/ラムダ |
| goto/gosub | サポート | 非推奨(関数を使用) |
変数宣言
; HSP
a = 10
b = 3.14
s = "Hello"
dim arr, 10
// HSPPP
int a = 10;
double b = 3.14;
std::string s = "Hello";
std::vector<int> arr(10);
制御構文
; HSP if
if a > 10 {
mes "big"
} else {
mes "small"
}
; HSP repeat
repeat 10
mes cnt
loop
// HSPPP if
if (a > 10) {
mes("big");
} else {
mes("small");
}
// HSPPP for
for (int cnt = 0; cnt < 10; cnt++) {
mes(std::to_string(cnt));
}
goto/gosub → 関数
; HSP
gosub *draw
stop
*draw
color 255, 0, 0
boxf 0, 0, 100, 100
return
// HSPPP
void draw() {
color(255, 0, 0);
boxf(0, 0, 100, 100);
}
void hspMain() {
screen(0, 640, 480);
draw(); // 関数呼び出し
return;
}
button の gosub → ラムダ
; HSP
button gosub "Click", *onclick
stop
*onclick
mes "Clicked!"
return
// HSPPP
button("Click", []() {
mes("Clicked!");
});
GUI変数の注意(shared_ptr必須)
; HSP(問題なし)
chk = 0
chkbox "Enable", chk
// HSPPP(shared_ptr必須)
auto chk = std::make_shared<int>(0);
chkbox("Enable", chk);
// 値の取得
if (*chk) {
mes("Enabled");
}
⚠️ なぜshared_ptrが必要か: HSPでは変数がグローバルなので問題ありませんが、C++ではローカル変数は関数を抜けると破棄されます。GUIコントロールが参照している変数が消えると、未定義動作(クラッシュ等)になります。
shared_ptrは参照カウントで変数の寿命を管理し、この問題を防ぎます。
移行チェックリスト
void hspMain()を定義したか- 変数はすべて型を指定して宣言したか
- 行末に
;を付けたか goto/gosubを関数に置き換えたかchkbox/combox/listboxはshared_ptrを使用しているか- ローカル変数を GUI コールバックで使用していないか
実践的なサンプル
シンプルなゲームループ
import hsppp;
using namespace hsppp;
int x = 320, y = 240;
void hspMain() {
screen(0, 640, 480);
title("Simple Game");
while (true) {
redraw(0);
color(255, 255, 255);
cls();
// プレイヤー描画
color(255, 0, 0);
circle(x - 20, y - 20, x + 20, y + 20, 1);
redraw(1);
// 入力処理
int key = stick(15); // 方向キーは押しっぱなしOK
if (key & stick_left) x -= 5;
if (key & stick_right) x += 5;
if (key & stick_up) y -= 5;
if (key & stick_down) y += 5;
// ESCで終了
if (key & stick_esc) break;
await(16); // 約60fps
}
return;
}
メソッドチェーンを活用した描画
import hsppp;
using namespace hsppp;
void hspMain() {
auto win = screen({.width = 800, .height = 600, .title = "Method Chain Demo"});
// メソッドチェーンで一気に描画
win.color(100, 100, 200)
.boxf() // 背景
.color(255, 255, 0)
.font("Arial", 48, 1)
.pos(200, 100)
.mes("HSPPP Demo")
.color(255, 255, 255)
.font("MS Gothic", 16, 0)
.pos(200, 200)
.mes("メソッドチェーンで")
.mes("連続して描画できます");
}
GUIフォーム
import hsppp;
using namespace hsppp;
void hspMain() {
screen(0, 400, 300);
title("GUI Form");
// 入力フィールド(shared_ptr必須)
auto name = std::make_shared<std::string>("");
auto selected = std::make_shared<int>(0);
auto checked = std::make_shared<int>(0);
color(0, 0, 0);
pos(20, 20);
mes("名前:");
pos(80, 18);
input(name, 200, 24);
pos(20, 60);
mes("性別:");
pos(80, 58);
combox(selected, 60, "男性\n女性\nその他");
pos(20, 100);
chkbox("メルマガを受け取る", checked);
pos(20, 150);
button("送信", [&]() {
mes("=== 入力内容 ===");
mes("名前: " + *name);
mes("性別: " + str(*selected));
mes("メルマガ: " + (*checked ? std::string("はい") : std::string("いいえ")));
});
return;
}
画像の読み込みと表示
import hsppp;
using namespace hsppp;
void hspMain() {
screen(0, 640, 480);
title("Image Demo");
// OOP版: Celオブジェクトで管理
Cel player = loadCel("player.png");
if (!player) {
mes("画像の読み込みに失敗しました");
return;
}
player.divide(4, 4); // 4x4分割
int frame = 0;
while (true) {
redraw(0);
cls(4);
// アニメーション
player.put(frame % 16, 100, 100);
redraw(1);
frame++;
if (getkey(VK_ESCAPE)) break;
await(100);
}
return;
}
エラーハンドリング
import hsppp;
using namespace hsppp;
void hspMain() {
screen(0, 640, 480);
// エラーハンドラを設定
onerror([](const HspError& e) {
color(255, 0, 0);
mes("=== エラー発生 ===");
mes(strf("コード: %d", e.error_code()));
mes("メッセージ: " + e.message());
mes(strf("場所: %s:%d", e.file_name().c_str(), e.line_number()));
});
// わざとエラーを発生させる
button("エラーを発生させる", []() {
color(-1, 0, 0); // 不正な色値 → HspError
});
}
内部アーキテクチャ
エントリポイントの隠蔽
ユーザーは main / WinMain を記述しません。ライブラリ側がブートストラップを提供します。
起動フロー:
1. [System Boot] ライブラリ内の WinMain が起動
2. [Init] COM, Direct2D Factory, システムフォント等の初期化
3. [User Code] hspMain() をコール
4. [Persistence] hspMain終了後もメッセージループ継続(stopと同等)
描画システム(Surface Architecture)
すべての描画対象を「Surface」として抽象化し、IDマップで管理します。
HspSurface (基底クラス)
├── HspWindow (screen/bgscr) - HWND + SwapChain
└── HspBuffer (buffer) - BitmapRenderTarget
ID管理: std::map<int, std::shared_ptr<HspSurface>>
- gsel(id): current_surface を切り替え
- gcopy(id, ...): ポリモーフィズムで画像転送
描画パイプライン(保持モード)
DirectXのImmediate Modeではなく、HSPのCanvas挙動を再現しています。
1. [Offscreen First] 描画命令はバックバッファへ
2. [Redraw Command]
- redraw(0): 描画バッチ開始
- redraw(1): バックバッファ → スワップチェーン転送 + VSync
3. [Auto-Repaint] WM_PAINTでバックバッファを自動再転送
Screen クラスの軽量ハンドル方式
Screen クラスは shared_ptr<HspSurface> を保持せず、ID のみを保持します。
class Screen {
int m_id; // ウィンドウID
bool m_valid; // 有効フラグ
// ...
};
これにより:
- モジュール境界の型問題を回避
- コピーコストを最小化(軽量コピー可能)
- HSPのウィンドウID概念と一致
トラブルシューティング
ビルドエラー
Q: import hsppp; でエラーになる
A: Visual Studio 2026 (VS 18) を使用していますか?VS 2022以前ではC++23 Modulesのサポートが不十分です。
Q: リンクエラーが発生する
A: プラットフォームが x64 になっているか確認してください。Win32 (x86) はサポートしていません。
実行時エラー
Q: ウィンドウが表示されない
A: screen_hide フラグが設定されていないか確認してください。また、hspMain 内でループせずにすぐ return していませんか?
Q: GUIコントロールをクリックするとクラッシュする
A: 変数に shared_ptr を使用していますか?ローカル変数をそのまま渡していると、関数を抜けた時点で破棄されます。
// NG
void setup() {
int chk = 0;
chkbox("Enable", &chk); // コンパイルエラーになるはず
}
// OK
auto chk = std::make_shared<int>(0);
chkbox("Enable", chk);
Q: 文字化けする
A: Hspppは UTF-8 を使用しています。ソースファイルが UTF-8 で保存されているか確認してください。Visual Studioでは「ファイル」→「名前を付けて保存」→「エンコード付きで保存」から設定できます。
パフォーマンス
Q: 描画が遅い
A:
redraw(0)で描画開始、redraw(1)で画面更新をしていますか?- 毎フレーム大量の
mesを呼んでいませんか?フォント切り替えはコストがかかります。
ライセンスとリンク
ライセンス
Boost Software License 1.0
非常に寛容なオープンソースライセンスです。商用利用、改変、再配布が自由にできます。著作権表示の保持のみが必要です。
リンク
- GitHub リポジトリ: https://github.com/Velgail/HSPPP_Lib
- HSP公式サイト: https://hsp.tv/
バージョン情報
// プログラムからバージョンを取得
auto ver = hsppp::get_version();
mes(strf("HSPPP v%d.%d.%d", ver.major, ver.minor, ver.patch));
mes(strf("Build: %s / %s", ver.build_type, ver.platform));
// または簡易版
mes(hsppp::version()); // "0.1.0"
現在のバージョン: 0.1.0 (2026-01-02)
以上が HSPPP の完全ガイドです。HSPユーザーがC++の世界へスムーズに移行できることを願っています。
コメント