ply-engine
Plyの実装と設計に関する総合的なガイドです。ply-engine API、plyx ワークフロー、またはRustを使用したPly UIの構築に関するタスクが発生した場合に、このスキルを活用できます。
description の原文を見る
Complete implementation and design guide for Ply. Use this skill whenever a task involves ply-engine APIs, plyx workflows, or building UI in Rust with Ply.
SKILL.md 本文
Ply Engine
このパートは Ply アプリケーションを書くための究極のガイドです。
Part 1: 譲歩できないルール
- Ply API を捏造しないこと。このスキルに記載されていないメソッドは発明しないこと。
- 常に以下を確認すること:
use ply_engine::prelude::*;
- フィーチャゲートを尊重すること。API がフィーチャを必要とする場合は、使用前にフィーチャが有効になっていることを確認すること。
- 生成されたコードのインデント方針:
- まず、既存のインデントスタイルを検出して保持すること。
- スタイルが不明確であるか、新しいスニペットを生成する場合は、デフォルトで 2 スペースインデントを使用すること。
- 同じブロック内でタブとスペースを混在させないこと。
- インタラクティブ要素とリストアイテムに明示的 ID を優先すること。
- すべての要素に明示的なサイジング設定が必要であること。
- ビルダークロージャはデフォルトでチェーンリターンスタイルを使用すること。
- これを実行:
.layout(|l| l .direction(TopToBottom) .align(CenterX, Top) .gap(12) .padding(14) ) - これは実行しないこと:
.layout(|l| { l.direction(TopToBottom) .align(CenterX, Top) .gap(12) .padding(14); l }) - 同じルールが
overflow、border、scrollbar、floating、effect、shader、text_input、accessibilityなどの他のビルダークロージャにも適用されます。
Part 2: フィーチャマトリックス
Ply crate フィーチャ:
a11y(デフォルト): アクセシビリティサポートtext-styling: インラインスタイリングとアニメーションタグtinyvg: TinyVG ベクターレンダリングshader-build: build.rs 用シェーダビルドパイプラインユーティリティbuilt-in-shaders: 組み込みシェーダアセットnet: HTTP + WebSocket APInet-json: ネットレスポンス用 JSON デシリアライゼーションヘルパーaudio: macroquad オーディオの再エクスポートstorage: クロスプラットフォーム永続ストレージ API
Part 3: アプリケーションスケルトン
これは plyx init で生成されるデフォルトプロジェクト構造です:
use ply_engine::prelude::*;
fn window_conf() -> macroquad::conf::Conf {
macroquad::conf::Conf {
miniquad_conf: miniquad::conf::Conf {
window_title: "Hello Ply!".to_owned(),
window_width: 800,
window_height: 600,
high_dpi: true,
sample_count: 4,
platform: miniquad::conf::Platform {
webgl_version: miniquad::conf::WebGLVersion::WebGL2,
..Default::default()
},
..Default::default()
},
draw_call_vertex_capacity: 100000,
draw_call_index_capacity: 100000,
..Default::default()
}
}
#[macroquad::main(window_conf)]
async fn main() {
static DEFAULT_FONT: FontAsset = FontAsset::Path("assets/fonts/MyFont.ttf");
let mut ply = Ply::<()>::new(&DEFAULT_FONT).await;
loop {
clear_background(BLACK);
let mut ui = ply.begin();
ui.element()
.width(grow!())
.height(grow!())
.layout(|l| l.align(CenterX, CenterY))
.children(|ui| {
ui.text("Hello, Ply!", |t| t.font_size(32).color(0xFFFFFF));
});
ui.show(|_| {}).await;
next_frame().await;
}
}
もちろんこれはしばしば異なる機能とコンポーネントを含む異なるファイルで拡張されます。
Part 4: Prelude
以下はすべてこのリポジトリの ply_engine::prelude からエクスポートされます。
4.1 コア型
PlyUiIdGraphicAssetFontAssetShaderAssetLerpcrate::easing::*のすべてのイージング関数
4.2 ユーティリティ
render_to_textureset_shader_source
4.3 サイジングマクロ
grow!fit!fixed!percent!
4.4 Glob 列挙型
AlignX::{Left, CenterX, Right}AlignY::{Top, CenterY, Bottom}BorderPosition::{Outside, Middle, Inside}LayoutDirection::{LeftToRight, TopToBottom}
4.5 型のみの再エクスポート
WrapModeAccessibilityRole
4.6 フィーチャゲート付き再エクスポート
built-in-shaders:*net:net,WsMessagestorage:Storagetext-styling:stylingaudio:*
4.7 常に再エクスポートされるモジュールとヘルパー
jobsモジュール- 完全な
macroquad::prelude::* - Ply
Color - macroquad color の
MacroquadColorエイリアス macroquadcrateset_mouse_cursorCursorIcon
重要: 完全な macroquad::prelude::* が再エクスポートされているため、Ply prelude を使用する際に任意の macroquad prelude 関数/型もスコープ内にあります。
Part 5: 完全な API リファレンス (Ply、Ui、ビルダー)
5.1 Ply コアメソッド
構築とライフサイクル:
Ply::new(default_font).awaitPly::new_headless(dimensions)begin() -> Uieval() -> Vec<RenderCommand<_>>show(handle_custom_command).await
ポインターとフォーカス:
pointer_over(id) -> boolpointer_over_ids() -> Vec<Id>focused_element() -> Option<Id>set_focus(id)clear_focus()is_pressed(id) -> boolis_just_pressed(id) -> boolis_just_released(id) -> bool
ID 別テキスト入力状態:
get_text_value(id) -> &strset_text_value(id, value)get_cursor_pos(id) -> usizeset_cursor_pos(id, pos)get_selection_range(id) -> Option<(usize, usize)>set_selection(id, anchor, cursor)
レイアウト、境界、スクロール:
set_layout_dimensions(dimensions)pointer_state(position, is_down)update_scroll_containers(drag_scrolling_enabled, scroll_delta, delta_time)bounding_box(id) -> Option<BoundingBox>scroll_container_data(id) -> Option<ScrollContainerData>set_scroll_position(id, position)
デバッグとパフォーマンス:
set_debug_mode(bool)set_debug_view_width(f32)is_debug_mode() -> boolset_culling(bool)max_element_count(u32)max_measure_text_cache_word_count(u32)set_measure_text_function(|text, config| -> Dimensions)
5.2 Ui メソッド
element() -> ElementBuildertext(text, |TextConfig| ... )scroll_offset() -> Vector2- 現在開いている要素コンテキストのインラインステートクエリ:
hovered()pressed()just_pressed()just_released()focused()
Ui は Ply へ参照外しされるため、すべての Ply メソッドは ui からも呼び出し可能です。
5.3 ElementBuilder メソッド (完全版)
レイアウトとサイジング:
width(Sizing)height(Sizing)aspect_ratio(f32)contain(f32)cover(f32)layout(|LayoutBuilder| ...)
ビジュアル:
background_color(color)corner_radius(f32 | (f32, f32, f32, f32))border(|BorderBuilder| ...)overflow(|OverflowBuilder| ...)image(ImageSource)effect(shader_asset, |ShaderBuilder| ...)shader(shader_asset, |ShaderBuilder| ...)rotate_visual(|VisualRotationBuilder| ...)rotate_shape(|ShapeRotationBuilder| ...)
構造と識別:
id(id_like)floating(|FloatingBuilder| ...)custom_element(data)children(|ui| ...) -> Idempty() -> Id
インタラクティビティ:
on_hover(|Id, PointerData| ...)on_press(|Id, PointerData| ...)on_release(|Id, PointerData| ...)on_focus(|Id| ...)on_unfocus(|Id| ...)preserve_focus()
テキスト入力とアクセシビリティ:
text_input(|TextInputBuilder| ...)accessibility(|AccessibilityBuilder| ...)
Part 6: サイジングとレイアウト API
6.1 Sizing とマクロ
fit!()またはfit!(min, max)または名前付き引数 (min:,max:)grow!()またはgrow!(min, max, weight)または名前付き引数 (min:,max:,weight:)fixed!(px)percent!(0.0..=1.0)
注釈:
grow!(weight: 0.0)は内部で fit として動作します。- 負の grow weight は無効です。
6.2 LayoutBuilder
gap(u16)align(AlignX, AlignY)direction(LayoutDirection)wrap()wrap_gap(u16)padding(u16 | (top, right, bottom, left))
6.3 OverflowBuilder
- クリッピング:
clip_x()、clip_y()、clip() - スクロール:
scroll_x()、scroll_y()、scroll() no_drag_scroll()scrollbar(|ScrollbarBuilder| ...)
6.4 ScrollbarBuilder
width(f32)corner_radius(f32)thumb_color(color)track_color(color)min_thumb_size(f32)hide_after_frames(u32)
6.5 FloatingBuilder
offset(Vector2-like)z_index(i16)anchor((AlignX, AlignY), (AlignX, AlignY))attach_parent()attach_root()attach_id(id)clip_by_parent()passthrough()
6.6 BorderBuilder
color(color)all(u16)left(u16)right(u16)top(u16)bottom(u16)between_children(u16)position(BorderPosition)
6.7 ローテーションビルダー
ビジュアルローテーション (rotate_visual):
degrees(f32)radians(f32)pivot((x, y))[0,1] で正規化flip_x()flip_y()
シェープローテーション (rotate_shape):
degrees(f32)radians(f32)flip_x()flip_y()
Part 7: テキスト API
7.1 ui.text + TextConfig
テキスト設定メソッド:
color(color)font(&'static FontAsset)font_size(u16)letter_spacing(u16)line_height(u16)wrap_mode(WrapMode)alignment(AlignX)effect(shader_asset, |ShaderBuilder| ...)accessible()
WrapMode バリアント:
WordsNewlineNone
7.2 カラー入力
Ply Color は以下を受け入れます:
0xRRGGBB整数(u8, u8, u8)(u8, u8, u8, u8)(f32, f32, f32)(f32, f32, f32, f32)
重要: float チャンネルは 0..1 ではなく 0..255 空間にあります。
Part 8: テキスト入力 API
8.1 TextInputBuilder
placeholder(&str)max_length(usize)password()multiline()drag_select()font(&'static FontAsset)font_size(u16)text_color(color)placeholder_color(color)cursor_color(color)selection_color(color)line_height(u16)scrollbar(|ScrollbarBuilder| ...)no_styles_movement()on_changed(|&str| ...)on_submit(|&str| ...)
8.2 text_input::styling モジュール (フィーチャ: text-styling)
重要なパブリック関数:
escape_str(s): 文字列内のすべてのスタイルデリミタをエスケープしますstrip_styling(s): すべてのスタイルタグを削除し、プレーンコンテンツを返しますcursor_to_content(s, pos): カーソル位置をコンテンツ文字インデックスに変換しますcontent_to_cursor(s, pos, snap_to_content): コンテンツ文字インデックスをカーソル位置に変換します。snap_to_contentが true の場合、カーソルは}などの構造的位置をスキップし、次の表示可能な文字に着地します。cursor_to_raw(s, pos): スタイル付きカーソル位置を文字列の raw インデックスに変換しますraw_to_cursor(s, pos): raw 文字列インデックスをスタイル付き文字列内のカーソル位置に変換します
異なる種類の位置:
raw position:{color=...|...}やエスケープ文字を含むマークアップを含むフルスタイル付き文字列のインデックス。cursor position: テキスト入力編集で使用される視覚カーソルインデックス。表示可能な文字とカーソルが通過できるスタイル構造位置を含みます。content position: スタイリングが削除された後の平文表示コンテンツのインデックス。
Part 9: ID、インタラクティビティ、状態
9.1 Id
Id::new(label)Id::new_index(label, index)Id::new_index_seed(label, index, seed)From<&'static str>およびFrom<(&str, u32)>
9.2 インラインステートクエリ
アクティブな .children(|ui| ...) スコープ内:
ui.hovered()ui.pressed()ui.just_pressed()ui.just_released()ui.focused()
9.3 コールバックイベント
.on_hover(|id, pointer| ...).on_press(|id, pointer| ...).on_release(|id, pointer| ...).on_focus(|id| ...).on_unfocus(|id| ...)
テキスト入力フォーカスを奪わないツールバーのようなコントロールには .preserve_focus() を使用します。
9.4 ID 別ステートクエリ
is_pressed(id)is_just_pressed(id)is_just_released(id)pointer_over(id)pointer_over_ids()bounding_box(id)scroll_container_data(id)
Part 10: レンダリング、アセット、シェーダ API
10.1 アセット
GraphicAsset:
GraphicAsset::Path("...")(Web でフレームスキップの原因となる可能性)GraphicAsset::Bytes { file_name, data }(推奨)
FontAsset:
FontAsset::Path("...")(Web でフレームスキップの原因となる可能性)FontAsset::Bytes { file_name, data }(推奨)
.image(...) で受け入れられる画像入力:
&'static GraphicAssetTexture2Dtinyvg::format::Image(フィーチャ:tinyvg)
10.2 シェーダ API
ShaderAsset バリアント:
Path(&'static str)(Web でフレームスキップの原因となる可能性)Source { file_name, fragment }(推奨)Stored(&'static str)
ShaderBuilder:
.uniform(name, value)値は以下のいずれか:f32[f32; 2][f32; 3][f32; 4]i32[[f32; 4]; 4]
ランタイムシェーダソース更新:
set_shader_source(name, fragment_source)- その後
ShaderAsset::Stored(name)経由で参照
10.3 レンダーユーティリティ
render_to_texture(width, height, || { draw calls }) -> Texture2D
10.4 組み込みシェーダ定数 (フィーチャ: built-in-shaders)
FOIL:u_time(必須)、u_speed(デフォルト1.0)、u_intensity(デフォルト0.3)HOLOGRAPHIC:u_time(必須)、u_speed(デフォルト1.0)、u_saturation(デフォルト0.7)DISSOLVE:u_threshold(必須)、u_edge_color(必須)、u_edge_width(デフォルト0.05)、u_seed(デフォルト0.0)GLOW:u_glow_color(必須)、u_glow_radius(デフォルト0.05)、u_glow_intensity(デフォルト1.0)CRT:u_line_count(デフォルト100.0)、u_intensity(デフォルト0.3)、u_time(推奨)GRADIENT_LINEAR:u_color_a(必須)、u_color_b(必須)、u_angle(デフォルト0.0、ラジアン)GRADIENT_RADIAL:u_color_a(必須)、u_color_b(必須)、u_center(デフォルト[0.5, 0.5])、u_radius(デフォルト0.5)GRADIENT_CONIC:u_color_a(必須)、u_color_b(必須)、u_center(デフォルト[0.5, 0.5])、u_offset(デフォルト0.0)、u_hardness(デフォルト0.0)
Part 11: アクセシビリティ API
11.1 AccessibilityRole バリアント
NoneButtonLinkHeading { level }LabelStaticTextTextInputTextAreaCheckboxRadioButtonSliderGroupListListItemMenuMenuItemMenuBarTabTabListTabPanelDialogAlertDialogToolbarImageProgressBar
11.2 AccessibilityBuilder メソッド
- ロールショートカット:
button(label: &str)heading(label: &str, level: u8)link(label: &str)static_text(label: &str)checkbox(label: &str)slider(label: &str)image(alt: &str)
- 汎用フィールド:
role(role: AccessibilityRole)label(text: &str)description(text: &str)value(text: &str)value_min(min: f32)value_max(max: f32)checked(checked: bool)
- フォーカスと順序:
focusable()tab_index(index: i32)
- 方向フォーカス:
focus_right(target: impl Into<Id>)focus_left(target: impl Into<Id>)focus_up(target: impl Into<Id>)focus_down(target: impl Into<Id>)
- フォーカスリング:
disable_ring()ring_color(color: impl Into<Color>)ring_width(width: u16)
- ライブリージョン:
live_region_polite()live_region_assertive()
Part 12: フィーチャモジュール (Net、Storage、Jobs、Audio)
12.1 ネットワーク (net)
HTTP:
net::get(id, url, |HttpConfig| ...)net::post(...)net::put(...)net::delete(...)net::request(id) -> Option<Request>
HttpConfig メソッド:
header(key, value)body(&str)body_bytes(Vec<u8>)
Request メソッド:
response() -> Option<Result<Arc<Response>, String>>cancel()
Response メソッド:
status() -> u16text() -> &strbytes() -> &[u8]json<T>()(フィーチャ:net-json)
WebSocket:
net::ws_connect(id, url, |WsConfig| ...)net::ws(id) -> Option<WebSocket>
WsConfig メソッド:
header(key, value)insecure()
WebSocket メソッド:
send(&[u8])send_text(&str)recv() -> Option<WsMessage>close()
WsMessage バリアント:
ConnectedText(String)Binary(Vec<u8>)Error(String)Closed
12.2 ストレージ (storage)
Storage::new(path).awaitsave_string(path, data).awaitsave_bytes(path, data).awaitload_string(path).awaitload_bytes(path).awaitremove(path).awaitexport(path).await
12.3 ジョブ (jobs)
jobs::spawn(id, || async move { ... }, |result| { ... })jobs::running(id)jobs::is_running(id)jobs::list()
12.4 オーディオ (audio)
audio フィーチャが有効な場合、macroquad オーディオ API は prelude にあります。
典型的な呼び出し:
load_sound(path).awaitload_sound_from_bytes(bytes).awaitplay_sound_once(&sound)play_sound(&sound, PlaySoundParams { .. })stop_sound(&sound)set_sound_volume(&sound, volume)
Part 13: Lerp とイージング API
13.1 Lerp トレイト
実装対象:
f32u16Vector2macroquad::prelude::Vec2(f32, f32, f32, f32)(u16, u16, u16, u16)Color
カラー固有ヘルパー:
Color::lerp_srgb(...)Color::lerp_oklab(...)
13.2 Prelude のイージング関数
ease_in_quad、ease_out_quad、ease_in_out_quadease_in_cubic、ease_out_cubic、ease_in_out_cubicease_in_quart、ease_out_quart、ease_in_out_quartease_in_sine、ease_out_sine、ease_in_out_sineease_in_expo、ease_out_expo、ease_in_out_expoease_in_back、ease_out_back、ease_in_out_backease_in_elastic、ease_out_elastic、ease_in_out_elasticease_out_bounce、ease_in_bounce、ease_in_out_bounce
Part 14: シェーダビルドパイプライン API
build.rs で build-dependencies としてフィーチャ shader-build を使用してください。
メイン API:
ShaderBuild::new().source_dir("...").output_dir("...").slangc_path("...").override_file_type_handler(ext, handler).build()
デフォルト:
- ソース:
shaders/ - 出力:
assets/build/shaders/ - SPIR-V temp:
build/shaders/spirv/
Part 15: AI 向け plyx CLI リファレンス
plyx が利用可能な場合は、セットアップとプラットフォームビルドには plyx を優先してください。
plyx initplyx add [args]plyx skill [--install]plyx apk [--native] [--install] [--auto]plyx webplyx ios [--device] [--actions] [--auto]plyx completions <shell> [--install]
15.1 plyx init
プロジェクトを作成し、Google Fonts からフォントを選択し、フィーチャフラグを選択します。
生成:
- Cargo.toml
- src/main.rs
- assets/fonts/<font>.ttf
- シェーダパイプラインが選択された場合、オプション build.rs と shaders/
- オプション
.claude/skills/ply-engine/SKILL.md
15.2 plyx add
インタラクティブモード (引数なし) または非インタラクティブ形式:
plyx add <feature-key>plyx add font <font name>
plyx テンプレートで認識されるフィーチャキー:
tinyvgbuilt-in-shadersshader-pipelinetext-stylingnetnet-json(netに依存)audiostorageskill
skill と shader-pipeline は plyx 概念であり、[dependencies] のフィーチャキーではありません。shader-pipeline は build-dependencies と build.rs を追加します。skill は .claude/skills/ply-engine/SKILL.md をプロジェクトにインストールします。
15.3 plyx web
リリース wasm をビルドし、build/web/ に出力:
build/web/app.wasmbuild/web/index.htmlbuild/web/ply_bundle.js- 存在する場合は
assets/をコピー
15.4 plyx apk
モード:
- Docker (デフォルト)
- ネイティブ (
--native)
有用なフラグ:
--installadb でインストール--auto非インタラクティブモード
APK 出力場所:
target/android-artifacts/release/apk/<crate>.apk
15.5 plyx ios
モード:
- デフォルトではシミュレータービルド
--deviceで実デバイス--actionsでワークフロー生成
要件:
- macOS + Xcode ツール (
xcrun) - 適切な Rust iOS ターゲット
--device: 署名アイデンティティ、プロビジョニングプロファイル、および権利
15.6 plyx skill
plyx skillはバンドルされたスキルテキストを stdout に出力します。plyx skill --installは~/.claude/skills/ply-engine/SKILL.mdにインストールします。
Part 16: 共通パターン
このセクションは Ply で構築するときに出現する共通パターンを示しています。これらはあくまで例であり、要件ではありません。状況が要求する場合は、これらのパターンから逸脱することは自由ですし、適切な値とバリエーションを使用してください。24 などの多くの定数とデフォルト値で示しており、これは実際のアプリで置き換えるべきものです。
16.1 カーソル
カーソルを操作するときは、一般的に以下のようなものを追加することをお勧めします:
use std::cell::RefCell;
thread_local! {
static CURSOR: RefCell<CursorIcon> = RefCell::new(CursorIcon::Default);
}
fn set_cursor(icon: CursorIcon) {
CURSOR.with(|c| *c.borrow_mut() = icon);
}
fn apply_cursor() {
CURSOR.with(|c| {
set_mouse_cursor(*c.borrow());
*c.borrow_mut() = CursorIcon::Default;
});
}
その後、イベントハンドラで set_cursor(...) を呼び出し、フレームの終わりで apply_cursor() を呼び出します。これでフレームの途中でカーソルが変わられず、カスタムロジックも可能になります。
16.2 スタイリング関数パターン
再利用可能なスタイリングは、ElementBuilder を取得して返す通常の Rust 関数である必要があります。
fn rounded(el: ElementBuilder<'_, ()>) -> ElementBuilder<'_, ()> {
el.corner_radius(12.0)
}
fn dark_bg(el: ElementBuilder<'_, ()>) -> ElementBuilder<'_, ()> {
el.background_color(0x2E2A28)
}
dark_bg(rounded(ui.element()))
.width(grow!())
.height(fixed!(60.0))
.children(|ui| {
ui.text("Styled with functions", |t| t.font_size(20).color(0xFFFFFF));
});
パラメータ化されたスタイルの場合:
fn my_style(el: ElementBuilder<'_, ()>, bg: u32, radius: f32) -> ElementBuilder<'_, ()> {
el.background_color(bg).corner_radius(radius)
}
my_style(ui.element(), 0x181515, 10.0)
.width(fit!())
.height(fit!())
.empty();
16.3 ボタン例
ボタンは以下のようなものを使用することをお勧めします:
fn button(ui: &mut Ui, id: (&str, u32), label: &str, mut on_click: impl FnMut() + 'static) {
ui.element().id(id).width(fit!()).height(fit!())
.on_press(move |_, _| on_click())
.accessibility(|a| a.button(label))
.children(|ui| {
let bg = if ui.pressed() {
LIGHT_PRIMARY_COLOR
} else if ui.hovered() || ui.focused() {
DARK_PRIMARY_COLOR
} else {
PRIMARY_COLOR
};
ui.element().width(fit!()).height(fit!())
.background_color(bg)
.corner_radius(SOME_RADIUS)
.layout(|l| l.padding(SOME_PADDING).align(CenterX, CenterY))
.children(|ui| {
ui.text(label, |t| t.font_size(24).color(0xFFFFFF));
});
});
}
実際のボタンをラッパー要素でラップすると、どこにいるか知ることができます。ラップはしばしば入力で便利です。
16.4 ポーリング HTTP 例
if user_data.is_none() {
net::get("user_data", "https://example.com/", |r| r
.header("Authorization", "Bearer token")
);
}
// ...
if user_data.is_none() {
if let Some(req) = net::request("user_data") {
match req.response() {
None => {
ui.text("{swing|Loading...}", |t| t.font_size(24).color(0xFFFFFF));
}
Some(Ok(resp)) => {
if resp.status() == 200 {
let data: UserData = resp.json().unwrap_or_else(|e| {
eprintln!("failed to parse user data: {e}");
UserData::default()
});
user_data = Some(data);
}
}
Some(Err(err)) => {
eprintln!("request failed: {err}");
}
}
}
}
16.5 ストレージ + ジョブ例
jobs::spawn(
"save_game",
|| async move {
Storage::new("my_app").await?
.save_string("save.json",
serde_json::to_string(&game_state).map_err(|e| e.to_string())?
).await
},
|result| {
if let Err(e) = result {
eprintln!("Failed to save game: {e}");
}
},
)?;
メインスレッドで Storage を使用するとウェブ上でフレームスキップが発生します。メインスレッドをブロックしないようにジョブ内でこのように使用するのが最善です。
16.6 テクスチャにレンダリング + シェーダ例
通常のレンダリングフロー用に静的シェーダアセットを使用します。
static TINT_SHADER: ShaderAsset = ShaderAsset::Source {
file_name: "tint.frag.glsl",
fragment: r#"#version 100
precision mediump float;
varying vec2 uv;
uniform sampler2D Texture;
uniform vec4 u_tint_color;
uniform float u_tint_strength;
void main() {
vec4 base = texture2D(Texture, uv);
vec3 mixed_rgb = mix(base.rgb, u_tint_color.rgb, clamp(u_tint_strength, 0.0, 1.0));
gl_FragColor = vec4(mixed_rgb, base.a);
}"#,
};
let chart = render_to_texture(400.0, 200.0, || {
clear_background(BLANK);
draw_line(0.0, 180.0, 400.0, 40.0, 3.0, GREEN);
draw_circle(200.0, 110.0, 8.0, RED);
});
ui.element()
.width(fixed!(400.0))
.height(fixed!(200.0))
.image(chart)
.effect(&TINT_SHADER, |s| s
.uniform("u_tint_color", [1.0, 0.85, 0.75, 1.0])
.uniform("u_tint_strength", 0.25f32)
)
.empty();
16.7 アクセシビリティ例
ui.element().id("first").width(fit!()).height(fit!())
.layout(|l| l.padding(SOME_PADDING))
.background_color(FIRST_BUTTON_COLOR)
.accessibility(|a| a.button("First").tab_index(1).focus_right("second"))
.on_press(|_, _| println!("first pressed"); )
.children(|ui| {
ui.text("First", |t| t.font_size(24).color(0xFFFFFF).accessible());
});
ui.element().id("second").width(fit!()).height(fit!())
.layout(|l| l.padding(SOME_PADDING))
.background_color(SECOND_BUTTON_COLOR)
.accessibility(|a| a.button("Second").tab_index(2).focus_left("first"))
.on_press(|_, _| println!("second pressed"); )
.children(|ui| {
ui.text("Second", |t| t.font_size(24).color(0xFFFFFF));
});
16.8 テキスト入力 + テキストスタイリングカーソルヘルパー
ui.element().width(fit!()).height(fit!())
.layout(|l| l.padding(BACKGROUND_PADDING))
.background_color(BACKGROUND_COLOR)
.children(|ui| {
ui.element().id("editor").width(fixed!(400.0)).height(fixed!(120.0))
.text_input(|t| t
.multiline()
.font_size(24)
.drag_select()
.no_styles_movement()
.on_changed(|text| println!("changed: {text}"))
)
.empty();
});
let raw = ui.get_text_value("editor").to_string();
let plain = styling::strip_styling(&raw);
let highlighted = plain.replace("TODO", "{color=#FFC32C|TODO}");
if raw != highlighted {
let cursor = ui.get_cursor_pos("editor");
let content_pos = styling::cursor_to_content(&raw, cursor);
ui.set_text_value("editor", &highlighted);
let new_cursor = styling::content_to_cursor(&highlighted, content_pos, true);
ui.set_cursor_pos("editor", new_cursor);
}
ラップすると背景のパディングを追加できます。テキスト入力に組み込みパディングがないため、パディングがないとテキストがきつく見えます。また、最初の行でビルダーが文字 t でスタイル化され、その後の行で .arguments() がどのようにスタイル化されるか注目してください。これは複数行にまたがるビルダーをスタイル化する方法です。
16.9 フローティングツールチップ
ui.element().id("tooltip_target").width(fit!()).height(fit!())
.layout(|l| l.padding(5))
.corner_radius(6.0)
.background_color(BUTTON_COLOR)
.children(|ui| {
ui.text("Hover me", |t| t.font_size(24).color(0xE8E0DC));
if ui.hovered() {
ui.element().width(fit!()).height(fit!())
.floating(|f| f
.attach_parent()
.anchor((CenterX, Top), (CenterX, Bottom))
.offset((0.0, -4.0))
)
.background_color(TOOLTIP_COLOR)
.corner_radius(6.0)
.layout(|l| l.padding(5))
.children(|ui| {
ui.text("Tooltip text", |t| t.font_size(16).color(0xFFFFFF));
});
}
});
16.10 ラップレイアウトとスクロールバー
ui.element().width(grow!()).height(grow!())
.layout(|l| l.wrap().wrap_gap(8).gap(8).padding(8))
.overflow(|o| o
.scroll_y()
.scrollbar(|s| s
.width(4.0)
.thumb_color(0xFFFFFF)
.track_color(0x222222)
.hide_after_frames(60)
)
)
.children(|ui| {
for i in 0..200 {
ui.element().id(("chip", i)).width(fit!()).height(fit!())
.layout(|l| l.padding(6))
.background_color(0x333333)
.corner_radius(6.0)
.children(|ui| {
ui.text(&format!("Chip {i}"), |t| t.font_size(16).color(0xFFFFFF));
});
}
});
16.11 GraphicAsset と FontAsset パターン
グラフィックおよびフォントアセットを static として宣言します。
static LOGO: GraphicAsset = GraphicAsset::Path("assets/images/logo.png");
static ICON_TVG: GraphicAsset = GraphicAsset::Bytes {
file_name: "icon.tvg",
data: include_bytes!("../assets/images/icon.tvg"),
};
static BODY_FONT: FontAsset = FontAsset::Path("assets/fonts/lexend.ttf");
static MONO_FONT: FontAsset = FontAsset::Bytes {
file_name: "jetbrains_mono.ttf",
data: include_bytes!("../assets/fonts/jetbrains_mono.ttf"),
};
// ...
ui.element().width(fixed!(96.0)).height(fixed!(96.0)).image(&LOGO).empty();
ui.element().width(fixed!(96.0)).height(fixed!(96.0)).image(&ICON_TVG).empty();
ui.text("Body", |t| t.font(&BODY_FONT).font_size(24).color(0xFFFFFF));
ui.text("Code", |t| t.font(&MONO_FONT).font_size(24).color(0xFFFFFF));
UI/UX プレイブック
このパートは機能的で真に記憶に残るインターフェースを作成するための決定的なリファレンスです。デザイン思考、美的方向性、基本原則、一般的な失敗モード、高度な UX 戦略、モーション デザイン、およびゲーム UI をカバーしています。
Part 1: デザイン思考
ビジュアル面に触れる前に、コンテキストを理解し、方向性にコミットします。
1.1 ブリーフを理解する
- 目的: このインターフェースは何の問題を解決しますか? 誰がそれを使用し、いつ?
- トーン: 明確な美的極端を選択し、それにコミットします。オプションには以下が含まれます: 極限ミニマル、マキシマリスト、レトロ未来的、有機的/自然、豪華/洗練、遊び心のある/おもちゃのような、編集的/雑誌的、ブルータリスト/生、アール デコ/幾何学的、ソフト/パステル、産業的/実用的。これらは出発点です。テンプレートではなく、文脈に真実であるものを設計してください。
- 制約: プラットフォーム、パフォーマンスニーズ、アクセシビリティ要件。
- 差別化: このインターフェースで誰かが覚えている唯一のものは何ですか? ビルドの前にそれに名前を付けてください。
1.2 コミット、躊躇しない
デザインの最大の失敗モードは臆病さであり、誰も傷つけず、誰も鼓舞しない選択をすることです。大胆なマキシマリズムと洗練されたミニマリズムの両方が有効です。失敗しているのは中間: 汎用的なレイアウト、安全なフォント、そして他のあらゆる製品のように見える均等に分布したパレット。
実行深度を美的ビジョンと一致させます。 マキシマリスト設計には、複雑なレイヤリング、豊富なアニメーション、複雑な構成が必要です。ミニマリスト設計には、レーザーのような鋭いスペーシング、完璧なタイプ、そして意図を持って行使された抑制が必要です。
Part 2: 基礎
2.1 シグニファイヤとアフォーダンス
- 自己説明型 UI: コンテナを使用して関係を知らせます。アイテムがコンテナを共有する場合、それらは関連していると認識されます。
- ビジュアル状態: 存在する場合、常にすべての 4 つのインタラクション状態を定義します: ホバー、アクティブ、無効、ロード中。不完全な状態システムは不完全な設計です。
- カーソルアイコン: カーソル変更を明示的に定義します。静的カーソルは壊れたシグニファイヤです。
- 示す、言わない: UI は、説明文なしの視覚的な手がかりだけでどのように機能するかを伝えるべきです。
2.2 ビジュアルヒエラルキーとコントラスト
- スキャン優先度: 高優先度情報を上部に配置します。サイズ、重さ、色を使用して目を導きます。
- コントラストエンジン: コントラストはヒエラルキーを作成します。大/小、カラフル/モノクロ、ヘビー/ライトを並置します。すべてが同じくらい重要なことはできません。
- フロー指標: ビジュアル指標を使用して、テキストなしで関係とフローを示します。
2.3 4 ポイントグリッド
- 標準スペーシング: すべてのスペーシングに 4 の倍数を使用します: 4、8、16、24、32、48、64 (UI がリサイズしても同じままでない限り)。
- セクション呼吸空間: 通常、視覚的に異なるセクション間で最小 32px を使用します。
- 近接度の意味: 関連する要素をしっかりグループ化します。関連のないものは明確に分離します。ラベルとその入力が離れている場合、関係が破断します。
2.4 色と深さ
- セマンティックカラー: 意味を一貫して割り当てます: 青 (アクション/インタラクティブ)、赤 (エラー/危険)、緑 (成功/確認)、黄 (警告/注意)。これらの期待を逆にしないでください。
- ダークモード深さ: 背景より背景表面を 明るく して深さを表現します (暗くではなく)。これは物理的に光がどのように機能するかを反映しています。
- 支配的+アクセント: 支配的な色と鋭いアクセント色は均等に分布したパレットより優れています。位置を選択し、それにコミットします。
2.5 タイポグラフィ
タイポグラフィはコミュニケーションアーキテクチャです。
カテゴリ別フォント性格:
- セリフ: 伝統的、古典的、編集的。印刷文脈またはヘリテージに傾いたブランドに最適。
- サンセリフ: モダン、読みやすい、デジタルネイティブ。スクリーンの標準。
- ディスプレイ/装飾的: 表現力豊か、インパクト大。ヘッドラインやヒーロー瞬間にのみ使用します。
- 時代遅れのフォント: 強い文化的遺産を持つフォントは避けてください (Comic Sans、Papyrus、Courier)。文脈に関係なく信頼性を損なわせます。
- 利用可能性: 通常、ユーザーがアセットに既に持っているフォントを使用します。ユーザーに適切なフォントがない場合は、
plyx add font NAMEを使用して Google Fonts からダウンロードします。
ペアリング &パレット:
- 通常、プロジェクトごとに 3 つ以下の書体を使用します。1 つで十分な場合がほとんどです。
- コントラストでペアリング: 大胆で特徴のあるディスプレイフェイスと静か で読みやすいボディフェイス。
- 単一の書体を使用する場合、太さ、サイズ、スタイル変動によってヒエラルキーを作成します。
- ジェネリックデフォルトを避けてください。 本物の性格を持つ書体に手を伸ばしてください。選択は検討済みに見える必要があります。デフォルトされていません。
Part 3: 美的実行
3.1 空間構成
標準的なグリッドレイアウトの先へ進みます。探索:
- 非対称とオーバーラップ: 境界を越えたり、グリッドを破ったりする要素はダイナミズムを作成します。
- 対角線フロー: まっすぐ下ではなく対角線に沿って目を導くと、エネルギーが生成されます。
- 豊かなネガティブスペース OR 制御された密度: 両方が機能します。失敗しているのは偶然のクラッターまたは偶然の空虚さです。
- 三分法則: 構成を 3×3 グリッドに分割することが多いです。交差点の近くに焦点を配置します。目は自然にこのパスに従います。
3.2 シェープ言語
- 幾何学的: 円と正方形は、コンポーネントとUI コンテナを信頼でき、構造化され、普遍的です。
- 有機的: 自由形状は、イラスト、ブランディングモーメント、またはそうでなければ厳密なレイアウトを柔らかくするのに最適です。
- 形は整理、分離、およびコンテンツに視覚的「重さ」を与えます。意図的に使用します。
3.3 テクスチャとフォーム
- テクスチャ は平らな設計に触覚と深さを追加します。背景、アイコン、アクセント領域に使用します - 一度にどこにでも使用しないでください。
- フォーム は光、影、遠近法を通じて三次元性を暗示します。
3.4 繰り返しと一貫性
デザイン全体で視覚要素 (カラーパレット、ヘッダースタイル、アイコンライブラリ、コーナー半径、スペーシングパターン) を繰り返します。繰り返しは怠け者ではなく、ブランディングです。ユーザーはインターフェースをデコードするのではなくコンテンツに従事できます。
Part 4: 回避するアンチパターン
これらは設計を素人としてマークする一般的な失敗モードです。
4.1 ビジュアルノイズ
- 虹グラデーション: マルチヒューグラデーションはほぼ常に意図しないように見えます。グラデーションを単一の色ファミリーのトーン変動に制限します。
- 不要なストローク/ボーダー: 特定のコントラストまたはコンテインメント目的を果たさないボーダーを削除します。疑わしい場合は削除します。
- エフェクトオーバーロード: 各ビジュアルエフェクトは注目に競争します。意味のあるものが着地するようにエフェクトをまばらに使用します。
4.2 矛盾
- コーナー半径カオス: 論理なく 4px、8px、16px、24px の半径を混在させると、破動的に見えます。
- 不一致なアセット: 異なるライブラリからのアイコン、または競合する照明とストロークスタイルのイラストは、ビジュアルの一貫性を破壊します。
- 冗長アフォーダンス: タッチインターフェースにスワイプ矢印を表示したり、既に万能アイコンを持つボタンをラベル付けしたりしないでください。
4.3 ユーザーフロー間隙
- 脱出ルートなし: すべてのオプションフローには「スキップ」または「今はいいです」パスが必要です。ユーザーをトラップすることは信頼侵害です。
- 空白検索状態: ユーザーが検索を開いたときに空白画面を表示しません。最近の検索、提案、またはカテゴリを提供します。
4.4 汎用美学
相互区別不可能な設計を作成する収束デフォルトを避けてください:
- 視覚的な壁紙になった使い古されたタイプフェース
- 白い背景の紫グラデーション
- 構成的な緊張のない予測可能なカードベースのレイアウト
- 排除ではなく意図によって選択されたように感じる安全で忘れられやすい色パレット
すべての設計は、*このコンテキ
ライセンス: 0BSD(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- TheRedDeveloper
- ライセンス
- 0BSD
- 最終更新
- 2026/4/19
Source: https://github.com/TheRedDeveloper/ply-engine / ライセンス: 0BSD
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。