{"id":"hakochanjp/rain-system","name":"rain-system","scope":"hakochanjp","platform":"roblox","description":"A weather-system module for Roblox specialized in dynamic rain","version":"0.11.5","latest":"0.11.5","versions":["0.1.0","0.2.0","0.2.1","0.3.0","0.4.0","0.5.0","0.6.0","0.7.0","0.8.0","0.8.1","0.9.0","0.9.1","0.10.0","0.10.1","0.11.0","0.11.1","0.11.2","0.11.3","0.11.4","0.11.5"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{"hakochanjp/rain-particle":{"version":"^0.3.0","alias":"RainParticle"},"roblox/rodux":{"version":"^3.0.0","alias":"Rodux"},"sleitnick/signal":{"version":"^2.0.3","alias":"Signal"},"sleitnick/trove":{"version":"^1.8.0","alias":"Trove"},"centau/vide":{"version":"^0.4.0","alias":"Vide"}},"integrity":"4aac64b190702342b0c7b0d43fcd7d49a5199b67685c150f859debd4e4f16235","likes":0,"downloads":0,"install":"forest install hakochanjp/rain-system","url":"https://forest.dev/p/roblox/hakochanjp/rain-system","files":"https://api.forest.dev/ai/package/roblox/hakochanjp/rain-system/files","readme":"# RainSystem\r\n\r\n雨が降ることに特化した Roblox 向け **ウェザーシステム** モジュール。\r\n\r\n> ℹ️ パーティクル単体の制御は別モジュール [`hakochanjp/rain-particle`](https://github.com/) を参照。RainSystem はそれらをまとめて「雨を降らせる体験」全体を扱う上位レイヤーを目指します。\r\n\r\n## ステータス\r\n\r\n✅ **v1 実装中** — クライアント完結型の `Init` / `SetState` / `Preset` / `Layers` API が利用可能。\r\nアセット（雨粒テクスチャ・雨音）は次の手順で同梱：`UploadFiles/asphalt/` に配置 → `task asphalt-sync`。\r\n\r\n## インストール（予定）\r\n\r\n利用側プロジェクトの `wally.toml`:\r\n\r\n```toml\r\n[dependencies]\r\nRainSystem = \"hakochanjp/rain-system@0.1.0\"\r\n```\r\n\r\n`wally install` 後に `Packages/RainSystem` が生成されます。\r\n\r\n## 使用例\r\n\r\n```lua\r\nlocal RainSystem = require(ReplicatedStorage.Packages.RainSystem)\r\n\r\n-- クライアントスクリプトで一度だけ呼ぶ\r\nRainSystem.Init({ shelterTarget = \"Character\" })\r\n\r\n-- プリセットで天候を切り替え（fade duration 秒）\r\nRainSystem.Preset.HeavyRain(3.0)\r\n\r\n-- 雲の量と雨の強さを個別指定（独立制御）\r\nRainSystem.SetState({ cloud = 0.8, rain = 0.45 }, 2.0)\r\n\r\n-- intensity ショートカットは cloud と rain を同値にセット（後方互換）\r\nRainSystem.SetState({ intensity = 0.45 }, 2.0)\r\n\r\n-- 状態変化を購読\r\nRainSystem.StateChanged:Connect(function(new, old)\r\n    print(string.format(\"cloud %.2f rain %.2f\", new.cloud, new.rain))\r\nend)\r\n\r\n-- パワーユーザー向け: レイヤー個別制御\r\nRainSystem.Layers.Atmosphere:SetEnabled(false)  -- 大気効果だけ切る（manageLighting=false 時は nil なので注意）\r\n\r\n-- 落雷をカスタム VFX にフック（Thunder.Struck シグナル）\r\nRainSystem.Layers.Thunder.Struck:Connect(function(info)\r\n    print(string.format(\"[Strike] dist=%.1f pos=%s\", info.distance, tostring(info.position)))\r\nend)\r\n\r\n-- 雨粒テンプレートを差し替え（Particle）／着水テンプレートを差し替え（Splash）\r\nRainSystem.Layers.Particle:SetTemplate(myCustomRainDropEmitter)\r\nRainSystem.Layers.Splash:SetTemplate(myCustomSplashEmitter)\r\n\r\n-- 終了\r\nRainSystem.Destroy()\r\n```\r\n\r\n### Lighting をホストに委譲する（`manageLighting`）\r\n\r\n利用側が Lighting / Atmosphere / 明るさを独自の環境システムで完全管理したい場合、\r\n`manageLighting = false` を指定すると RainSystem は Lighting サービスと Atmosphere\r\nインスタンスの定常状態を一切変更しない。\r\n\r\n```lua\r\nRainSystem.Init({ manageLighting = false })\r\n```\r\n\r\n- `false` のとき: `Lighting.Brightness` / `Atmosphere.Density` / `RainSystem_ColorCorrection`\r\n  を作成も変更もしない。Thunder のグローバル Brightness フラッシュも行わない\r\n  （`RainSystem.Layers.Atmosphere` は `nil` を返す）。\r\n- 雨粒・雲・着水・水たまり・波紋・雨音・雷の PointLight / 3D 音・画面水滴は従来どおり動作する。\r\n- **Cloud（`Workspace.Terrain.Clouds`）は対象外**。rain 固有の視覚として RainSystem が管理を継続する。\r\n- 既定は `true`（省略時は現状の挙動を維持）。\r\n\r\n### Atmosphere の見た目を調整する（`atmosphere`）\r\n\r\nAtmosphere レイヤーの補間端点（晴れ `*Clear` / 嵐 `*Storm` 時の値）を `Init` で上書きできる。\r\n省略したフィールドは既定値（`brightnessClear=2.0` / `brightnessStorm=0.6` / `saturationClear=0.0` /\r\n`saturationStorm=-0.3` / `densityClear=0.0` / `densityStorm=0.45`）が使われる。\r\n\r\n```lua\r\nRainSystem.Init({ atmosphere = { brightnessStorm = 0.4, densityStorm = 0.6 } })\r\n```\r\n\r\n`manageLighting = false` 時は Atmosphere レイヤー自体を生成しないため、この設定は無視される。\r\n\r\n### 雨音・雷音を SoundGroup に流す（`soundGroup`）\r\n\r\n雨音（outdoor/indoor）と雷音を、利用側が用意した `SoundGroup` 配下に接続できる。\r\n設定画面の音量スライダー等でグループ Volume を一括制御している場合、雨/雷の音も\r\nそれに追従するようになる（RainSystem 内部の音量計算とグループ Volume が乗算で効く）。\r\n\r\n```lua\r\n-- SoundGroup インスタンスを直接渡す\r\nRainSystem.Init({ soundGroup = SoundService.SFX })\r\n\r\n-- もしくは SoundService 直下の SoundGroup 名（string）で渡す\r\nRainSystem.Init({ soundGroup = \"SFX\" })\r\n\r\n-- ランタイムに変更 / 解除も可能\r\nRainSystem.SetSoundGroup(SoundService.BGM)\r\nRainSystem.SetSoundGroup(\"SFX\")\r\nRainSystem.SetSoundGroup(nil)        -- 未接続に戻す\r\nprint(RainSystem.GetSoundGroup())    -- 現在の SoundGroup（未設定なら nil）\r\n```\r\n\r\n- `SetSoundGroup` 後は生成済みの常設 Sound（outdoor/indoor）にも反映され、以後の落雷 Sound も最新の group を読む。\r\n- string が `SoundService:FindFirstChild` で解決できなければ **warn のみで未接続のまま継続**（クラッシュしない）。\r\n- 省略 / `nil` のときは SoundGroup 未設定（従来どおりの挙動）。\r\n\r\n### 屋内/屋外の雨表現\r\n\r\n雨粒自体は RainParticle の粒単位オクルージョン（Raycast）で制御される: 屋内ではスポーンせず、屋根面でちょうど消えるため、屋内にいても窓越しに外の雨が見える。天候全体が屋内で止まることはない。\r\n\r\n音・カメラ水滴・水たまり・波紋・着水・雷の減衰などの演出は、`ShelterState.smoothed`（頭上遮蔽率 `ratio` を時定数 τ=1 秒で時間平滑化した連続値。毎 Heartbeat 更新）を参照し、屋内外の境界をなめらかに遷移する（瞬時切替・チラつきなし）。\r\n\r\n遮蔽判定の追跡対象（頭上 Raycast の起点）はランタイムに切り替え可能。ファーストパーソン / 自由視点カメラの上方判定にも使える。\r\n\r\n```lua\r\nRainSystem.Init({ shelterTarget = \"Character\" })  -- 既定はキャラクター\r\nRainSystem.SetShelterTarget(\"Camera\")              -- カメラ位置で遮蔽判定\r\nprint(RainSystem.GetShelterTarget())               -- \"Camera\"\r\n```\r\n\r\n### Debug UI（Vide ベース）\r\n\r\n開発時に天候パラメータをリアルタイム操作できるフローティングパネルを表示。\r\nScrollingFrame 内に State / Preset / Cloud / Rain / Fade / Wind / ShelterTarget / Layer toggle / Strike / Recent log を一覧。\r\n\r\n```lua\r\nRainSystem.ShowDebug()   -- パネル表示\r\nRainSystem.HideDebug()   -- パネル非表示\r\n```\r\n\r\n### 公開レイヤー一覧\r\n\r\n`RainSystem.Layers.*` 経由でアクセス可能（全レイヤーが `:SetEnabled(bool)` を持つ）：\r\n\r\n| Layer | 役割 |\r\n| :--- | :--- |\r\n| `Atmosphere` | Lighting / Atmosphere の色・密度・空ティント |\r\n| `Audio` | 屋外/屋内別の雨音 BGM クロスフェード |\r\n| `CameraDrop` | 画面に張り付く水滴 GUI |\r\n| `Cloud` | `Workspace.Terrain.Clouds` 連動 |\r\n| `Particle` | 雨粒 ParticleEmitter（`SetTemplate` / `SetTexture` / `SetShelterTarget`） |\r\n| `Puddle` | 地面の水たまり Part 散布（距離ベース despawn 対応） |\r\n| `Ripple` | 水面着水時の同心円 Decal |\r\n| `Splash` | 着水パーティクル（`SetTemplate` / `GetTemplate`） |\r\n| `Thunder` | 落雷フラッシュ + 3D 音響、`Struck` シグナル + `TriggerStrike()` |\r\n\r\n## 開発\r\n\r\n```bash\r\nrokit install                  # ツールチェーン（rojo / wally / stylua / selene / asphalt / task）を導入\r\nwally install                  # 依存パッケージを取得\r\ntask serve                     # dev.project.json で Rojo serve + sourcemap watch\r\n```\r\n\r\n### Lint / Format\r\n\r\n| サブコマンド | 用途 |\r\n| :--- | :--- |\r\n| `task selene`   | `selene src tests` を実行（src と tests の静的解析） |\r\n| `task lint`     | `selene src tests` ＋ `stylua src tests --check`（CI 向けの非破壊チェック） |\r\n| `task lint-fix` | `stylua src tests` でフォーマット差分を**自動修正**（selene 警告は手動対応） |\r\n\r\n`task lint` がフォーマット差分で fail したら `task lint-fix` で一括解消できます（selene の警告は別途コード修正が必要）。\r\n\r\n### アセット管理（Asphalt）\r\n\r\n画像・音声などのアセットは [Asphalt](https://github.com/jackTabsCode/asphalt) で管理しており、\r\n`UploadFiles/asphalt/` 配下のファイルを Roblox クラウドにアップロードして\r\n`src/Assets.luau` を自動生成します。生成された Assets.luau は wally 配布対象に含まれます。\r\n\r\n#### パーティクル画像の再生成（Python + PIL）\r\n\r\n`UploadFiles/asphalt/images/` 配下の PNG（raindrop / splash_ground / splash_water / splash_metal / camera_drop）は [`scripts/gen_assets.py`](./scripts/gen_assets.py) で **deterministic に生成** されています。テクスチャを微調整したい場合は Python スクリプトを編集して再実行 → `task asphalt-sync` の順で反映します。\r\n\r\n```bash\r\npython scripts/gen_assets.py    # 5 つの PNG を 512x512 で再生成\r\ntask asphalt-sync               # Roblox クラウドに反映 + Assets.luau 更新\r\n```\r\n\r\n> 依存: Python 3 + Pillow (`pip install Pillow`)。出力は乱数を使わないので何度実行してもバイト同一です（[[asset-generation]] の 1:1 方針を尊守）。\r\n\r\n\r\n```bash\r\ncp .env.example .env           # ASPHALT_API_KEY を埋める（Roblox Creator Dashboard で発行）\r\ntask asphalt-dry-run           # アップロード予定を確認\r\ntask asphalt-sync              # 実アップロード + src/Assets.luau 再生成\r\n```\r\n\r\n| サブコマンド | 用途 |\r\n| :--- | :--- |\r\n| `task asphalt-sync` | クラウドへアップロードし `src/Assets.luau` と `asphalt.lock.toml` を更新 |\r\n| `task asphalt-dry-run` | 変更予定のみ確認（実アップロードしない） |\r\n| `task asphalt-debug` | `.asphalt-debug/` にローカル出力（クラウド・Studio 不要） |\r\n\r\n> ℹ️ `asphalt.toml` の `[creator]` はデフォルトで rain-system 配布主体のユーザー ID を指しています。グループ運用に切り替える場合は `type = \"group\"` に変えた上で、Roblox Creator Dashboard 側で API キーの **Eligible Creators** にそのグループを追加してください。\r\n\r\n### ディレクトリ構成\r\n\r\n```\r\nRobloxRainSystem/\r\n├── src/                  # ライブラリ本体（wally で配布）\r\n│   ├── init.luau         # 公開 API（Init / SetState / Preset / Layers / Debug 等）\r\n│   ├── Assets.luau       # Asphalt が自動生成（コミット推奨／手動編集禁止）\r\n│   ├── Debug.luau        # Vide ベースの開発者パネル\r\n│   ├── Types.luau        # 公開・内部型定義（WeatherState / LayerDeps 他）\r\n│   ├── State/            # Rodux Store + fade Manager + Preset\r\n│   ├── Shelter/          # 屋内/屋外判定（CheckPoint / Track）\r\n│   └── Layers/           # 9 レイヤー（Atmosphere / Audio / CameraDrop /\r\n│                         #             Cloud / Particle / Puddle / Ripple /\r\n│                         #             Splash / Thunder）\r\n├── tests/                # 動作確認スクリプト（wally 配布対象外）\r\n│   ├── RainSystemTest/   # Studio Play Solo 用の統合シナリオ\r\n│   │   ├── init.client.luau\r\n│   │   ├── LightningEffect.model.json   # 落雷 VFX テンプレ\r\n│   │   └── RainParticles.model.json     # 雨粒 / 着水テンプレ\r\n│   └── Unit/             # pure-logic スモークテスト（21+ checks）\r\n│       └── init.client.luau\r\n├── docs/superpowers/\r\n│   ├── specs/2026-05-25-rain-system-design.md  # v1 設計仕様\r\n│   └── plans/2026-05-25-rain-system-v1.md      # 初期実装計画\r\n├── UploadFiles/\r\n│   └── asphalt/          # Asphalt アップロード対象（images/ sounds/）\r\n├── CHANGELOG.md          # バージョン別変更履歴\r\n├── default.project.json  # リリース用 Rojo 設定（src のみ）\r\n├── dev.project.json      # 開発用 Rojo 設定（src + tests + Packages）\r\n├── asphalt.toml          # Asphalt 設定\r\n├── asphalt.lock.toml     # アセットIDロック（コミット必須）\r\n├── Taskfile.yaml         # 開発タスク（serve / lint / asphalt-sync 等）\r\n├── wally.toml\r\n├── rokit.toml\r\n├── stylua.toml\r\n├── selene.toml\r\n├── .env.example          # ASPHALT_API_KEY テンプレート\r\n├── LICENSE\r\n└── README.md\r\n```\r\n\r\n## リファレンス\r\n\r\n| ドキュメント | 内容 |\r\n| :--- | :--- |\r\n| [`CHANGELOG.md`](./CHANGELOG.md) | バージョン別の Added / Changed / Fixed / Performance / Tests |\r\n| [`docs/release-flow.md`](./docs/release-flow.md) | Wally publish の手順（lint / version bump / Unreleased 置換 / tag / publish） |\r\n| [`docs/superpowers/specs/2026-05-25-rain-system-design.md`](./docs/superpowers/specs/2026-05-25-rain-system-design.md) | v1 設計仕様（ゴール・原則・アーキテクチャ・レイヤー差し込み口） |\r\n| [`docs/superpowers/plans/2026-05-25-rain-system-v1.md`](./docs/superpowers/plans/2026-05-25-rain-system-v1.md) | 初期実装計画（タスク分解 / 受け入れ条件） |\r\n| `tests/Unit/init.client.luau` | Studio Play Solo で走る pure-logic スモークテスト（PASS/FAIL を Output に出力） |\r\n| `tests/RainSystemTest/init.client.luau` | Debug UI 起動 + テンプレ適用 + 落雷ハンドラ配線の統合シナリオ |\r\n\r\n> 両テストは `dev.project.json` で `StarterPlayer.StarterPlayerScripts.Tests` 配下に配置されるため、**Play Solo 1 回で同時に実行**されます。Output ウィンドウに `[PASS] / [FAIL]`（Unit）と `[Scenario]`（RainSystemTest）の両方のログが混在して出ます。\r\n\r\n## ライセンス\r\n\r\nMIT License © 2026 hakochanjp\r\n","readmeTruncated":false}