見出し画像

指定子と属性

指定子と属性の概要、Verse コードに追加のセマンティクスおよび動作を適用する方法について説明します。

Verse の 指定子 は、セマンティクス に関連する動作を記述し、識別子 および特定の キーワード に追加することができます。指定子の構文は、< と > でキーワードを囲みます。たとえば、IsPuzzleSolved()<decides><transacts> : void と記述します。

Verse の 属性 は、Verse 言語以外で使用される動作を記述します (Verse セマンティクスを記述する指定子とは異なる)。属性 構文 では、@ の後に @editable などの キーワード が続きます。

以下のセクションでは、Verse のすべての指定子および属性と、それらを使用できる場合について説明します。

理解1

指定子と属性について

指定子と属性の基本的な違い

指定子 `<keyword>`

  • Verse言語の内部的な動作を制御

  • `<>`で囲んで表現

  • 関数の振る舞いを定義

属性 `@keyword`

  • 外部システムとの連携に関する設定

  • `@`で始まる

  • UEFNのエディタやエンジンとの連携を制御

具体例で学ぶ指定子

1. 基本的な関数での指定子

# パブリック関数の定義
CheckPuzzle<public>()<decides>: bool {
    return # パズルの状態チェック
}

2. 複数の指定子の組み合わせ

# データを変更する判定関数
UpdateScore<public>(score: int)<decides><transacts>: bool {
    if (score > 0) {
        UpdatePlayerScore(score)
        return true
    }
    return false
}

実践的な属性の使用例

1. エディタでの編集可能な変数

# ゲーム内で調整可能なパラメータ
@editable
var MaxHealth: float = 100.0

@editable
var PlayerSpeed: float = 5.0

2. コンポーネントの設定

# デバイスコンポーネントの定義
@component
MaxHealthDevice := class:
    @editable
    var Health: float = 100.0

よく使用される指定子と属性の組み合わせ

ゲームロジックの例

# プレイヤーの状態管理
@component
PlayerState := class:
    @editable
    var InitialHealth: float = 100.0
    
    # 公開関数でヘルス更新
    UpdateHealth<public>(amount: float)<transacts>: void {
        InitialHealth += amount
    }
    
    # 状態チェック
    IsAlive<public>()<decides>: bool {
        return InitialHealth > 0.0
    }

主な指定子の使用場面

  1. `<public>`

    • 他のモジュールから呼び出せる関数の定義

    • APIとして公開する機能の作成

  2. `<decides>`

    • 条件判定を行う関数

    • if文で使用する判定処理

  3. `<transacts>`

    • データを変更する処理

    • ゲーム状態を更新する操作

主な属性の使用場面

  1. `@editable`

    • UEFNエディタで値を調整可能に

    • デザイナーが調整できるパラメータ

  2. `@component`

    • ゲームコンポーネントの定義

    • デバイスやアクターの機能実装

実践的なヒント

  1. 命名規則

# 分かりやすい命名で機能を明確に
@component
GameManager := class:
    @editable
    var GameDuration: float = 300.0  # 5分のゲーム時間
  1. 適切な組み合わせ

# 機能と設定の適切な組み合わせ
@component
ScoreSystem := class:
    @editable
    var PointsPerKill: int = 100

    AddScore<public>(kills: int)<transacts>: void {
        # スコア計算と更新
    }

注意点

  1. 指定子の使用

  • 必要な指定子のみを使用

  • 過剰な指定は避ける

  • 順序を正しく守る

  1. 属性の使用

  • エディタでの表示・編集が必要な場合のみ@editableを使用

  • コンポーネントは適切に@componentを付ける

  • 不必要な属性は避ける

まとめ

  • 指定子はVerse言語の内部動作を制御

  • 属性はUEFNエディタとの連携を制御

  • 適切な使用で保守性の高いコードを実現

  • 必要最小限の指定子と属性を使用することが重要

これらの機能を理解し適切に使用することで、より効率的なゲーム開発が可能になります。

指定子と属性の違いについて

1. 基本的な違い

指定子 `<keyword>`

# Verseの言語機能に関する制御
function<public>(): void
  • 目的: Verse言語内部の動作制御

  • 記法: `< >`で囲む

  • スコープ: 言語レベルのセマンティクス

  • 影響: コードの実行時の振る舞いを定義

属性 `@keyword`

# エンジンやエディタとの連携
@editable
var Speed: float = 10.0
  • 目的: 外部システムとの連携

  • 記法: `@`プレフィックス

  • スコープ: UnrealエンジンやUEFNエディタとの連携

  • 影響: 開発環境での取り扱いを定義

2. 使用場面の違い

指定子の使用場面

# 関数の振る舞いを定義
CheckCondition<public>(value: int)<decides><transacts>: bool {
    # 処理内容
}
  • 関数の可視性制御

  • 実行時の動作定義

  • データ操作の方法指定

  • 非同期処理の制御

属性の使用場面

@component
GameController := class:
    @editable
    var GameTime: float = 300.0
    
    @replicated
    var Score: int = 0
  • エディタでの編集可能項目の指定

  • コンポーネントの定義

  • ネットワーク同期の設定

  • エンジン機能との連携

3. 影響範囲の違い

指定子の影響

# コードの実行時動作に影響
UpdateHealth<public>(amount: float)<transacts>: void {
    # トランザクション処理
}
  • プログラムの実行フロー制御

  • メモリ管理への影響

  • スレッド処理の制御

  • エラーハンドリングの方法

属性の影響

@component
PlayerStats := class:
    @editable
    @tooltip("プレイヤーの最大体力")
    var MaxHealth: float = 100.0
  • エディタでの表示・編集方法

  • デバッグ情報の提供

  • エンジン機能との統合

  • ツール連携の設定

4. 具体的な使用例の比較

指定子を使用したゲームロジック

# ゲームの核となる処理
GameLogic<public> := class:
    ProcessTurn<public>(action: string)<transacts><decides>: bool {
        # ターン処理のロジック
        return true
    }

属性を使用したUI設定

# UI要素の設定
@component
UIController := class:
    @editable
    @tooltip("ボタンの色")
    var ButtonColor: color = #FF0000
    
    @editable
    @range(0.0, 1.0)
    var Opacity: float = 1.0

5. 主な特徴の比較表

| 特徴 | 指定子 | 属性 |
|------|--------|------|
| 構文 | `<keyword>` | `@keyword` |
| 主な用途 | コードの動作制御 | 外部システム連携 |
| 影響範囲 | 実行時の動作 | 開発環境での動作 |
| 使用場所 | 関数、メソッド | クラス、変数、プロパティ |
| 実行時影響 | あり | 限定的/なし |

6. 使用時の注意点

指定子使用時の注意

  • 必要最小限の指定子を使用

  • 順序を正しく守る

  • 矛盾する組み合わせを避ける

  • パフォーマンスへの影響を考慮

属性使用時の注意

  • エディタでの使用目的を明確に

  • 適切なデフォルト値の設定

  • ドキュメンテーションの提供

  • パフォーマンスへの影響を最小限に

まとめ

  • 指定子はVerse言語の内部動作を制御する言語機能

  • 属性は外部システムとの連携を制御する機能

  • 両者は異なる目的で使用され、相補的な役割を持つ

  • 適切な使い分けが効率的な開発につながる

これらの違いを理解することで、より効果的なVerseコードの作成が可能になります。

指定子と属性をお弁当箱に例える

🍱 お弁当箱で理解する指定子と属性

指定子 `<keyword>` はお弁当の「仕切り」

function<public>(): void

😄 例えるなら...

  • お弁当箱の仕切りのようなもの

  • 食べ物(コード)の振る舞いを決める

  • どの食べ物がどこにあるか整理する

🍱 具体例

  • `<public>` = 誰でも食べていいおかず

  • `<private>` = 自分だけの特別なおかず

  • `<decides>` = おかずを食べるか決める仕切り

  • `<transacts>` = おかずを入れ替える時の仕切り

@属性はお弁当箱の「シール」や「メモ」

@editable
var Speed: float = 10.0

😄 例えるなら...

  • お弁当箱に貼るシールやメモ

  • 外から見てわかる情報

  • 特別な注意書きや説明

🏷️ 具体例

  • `@editable` = 「中身を変えていいよ」シール

  • `@component` = 「これは特別なお弁当」シール

  • `@tooltip` = 「食べ方の説明」メモ

🎮 ゲーム開発での具体例

お弁当箱システム

# お弁当箱を作る
@component  # 「これは特別なお弁当箱です」シール
LunchBox := class:
    # 中身を変えていいおかず
    @editable  # 「好きに変えていいよ」シール
    var Rice: float = 100.0
    
    # おかずを追加する機能
    AddFood<public><transacts>(): void {
        # おかずを追加する処理
    }
    
    # お弁当が空っぽか確認
    IsEmpty<public><decides>: bool {
        # 中身チェック
    }

🌟 まとめ

指定子 `<keyword>`

  • お弁当の仕切りみたいなもの

  • 中身(コード)の整理に使う

  • どう動くかを決める

属性 `@keyword`

  • お弁当箱のシールみたいなもの

  • 外から見てわかる情報

  • 特別な注意書き

😊 覚え方のコツ

  1. 指定子 = 「中身」に関すること

    • お弁当の仕切りで整理

    • 中身の扱い方を決める

  2. 属性 = 「外側」に関すること

    • シールやメモで情報を伝える

    • 外からの扱い方を示す

🎯 実践例

# お弁当システム
@component  # 「特別なお弁当」シール
GameLunchBox := class:
    @editable  # 「好きに変えていいよ」シール
    var FoodAmount: int = 5

    # みんなが使える機能(public)で
    # 食べられるか確認(decides)して
    # 中身を変える(transacts)
    EatFood<public><decides><transacts>(): bool {
        if (FoodAmount > 0) {
            FoodAmount -= 1
            return true  # 食べられた!
        }
        return false  # 食べ物がない...
    }

このように、お弁当箱に例えることで、指定子と属性の違いが直感的に理解できますね!

  • 指定子は中身の整理と動作の決定

  • 属性は外からの見え方と扱い方の指定

これで指定子と属性の基本的な違いがバッチリ理解できたはずです!🎉

指定子を使う理由について 🎮

指定子を使う主な理由

1. 安全性の確保 🛡️

# 安全な処理の例
UpdatePlayerHealth<transacts><decides>(): bool {
    if (IsValidHealth()) {
        Player.Health -= Damage
        return true
    }
    return false
}
  • エラーが起きても安全に元に戻せる

  • データの整合性を保てる

  • バグの予防になる

2. コードの意図を明確に伝える 📝

# 関数の動作が一目で分かる
GetRandomReward<varies>(): int {
    return RandomInt(1, 100)
}
  • 他の開発者が見ても分かりやすい

  • コードの目的が明確

  • メンテナンスがしやすい

3. 実行時の動作保証 ⚡

# 必ず終了する計算
CalculateScore<converges>(points: int): int {
    return points * multiplier
}
  • 関数の動作が予測可能

  • パフォーマンスの予測が可能

  • 信頼性の高いコード

具体例で理解する 🎯

RPGゲームでの例

# 指定子なしの場合(危険)
BuyItem(): void {
    Player.Gold -= ItemPrice
    Inventory.AddItem(newItem)
    # エラーが起きたら?
    # データが壊れる可能性がある!
}

# 指定子ありの場合(安全)
BuyItem<transacts><decides>(): bool {
    if (Player.Gold >= ItemPrice) {
        Player.Gold -= ItemPrice
        Inventory.AddItem(newItem)
        return true
    }
    return false
    # エラーが起きても安全に元に戻る
}

まとめ 🌟

指定子を使う理由は主に3つ:

  1. 安全性

    • エラーからの回復が可能

    • データの破損を防ぐ

    • バグを減らせる

  2. 可読性 📖

    • コードの意図が明確

    • チーム開発が楽になる

    • メンテナンスが容易

  3. 信頼性 🎯

    • 動作の保証

    • 予測可能な振る舞い

    • デバッグのしやすさ

指定子は「ゲームの説明書」のようなもので、コードがどのように動作するかを明確に示すことで、より安全で信頼性の高いゲームを作ることができます!

指定子の省略について 🎮

基本的な仕組み

  • 指定子を書かない場合は自動的に「No Rollback」(デフォルト)として扱われます

  • エラーにはなりませんが、安全性が低くなる可能性があります

デフォルトの動作(No Rollback)の特徴

# 指定子なしの関数(暗黙的にNo Rollback)
UpdateScore(): void {
    Player.Score += 10
}

以下の性質を持ちます:

  • DIVERGES(制御フローの分岐可能)

  • VARIES(結果が変わる可能性あり)

  • READS(データの読み取り可能)

  • WRITES(データの書き込み可能)

  • NO_ROLLBACK(失敗時に元に戻せない)

推奨される使い方

  1. データを変更する場合

# 推奨:明示的に指定子を使用
UpdatePlayerData<transacts><decides>(): bool {
    # 安全な更新処理
}
  1. 単純な表示処理の場合

# デフォルトでも問題ない
DisplayMessage(): void {
    Print("Hello")
}

まとめ

  • 指定子を書かなくてもエラーにはなりません

  • ただし、データの安全性を考慮する場合は適切な指定子を使用することを推奨します

  • 特に重要なデータを扱う場合は、明示的に指定子を指定することでバグを防ぎやすくなります

理解2


エフェクト指定子

Verse の エフェクト は、関数 の公開が許可される動作のカテゴリを示します。エフェクト指定子は、次のものに追加できます。

  • 関数定義の名前の後にある ():name()<specifier> : type = codeblock

  • class キーワード:name := class<specifier>():

エフェクトの指定子は、次の 2 つのカテゴリに分けられます。

  • 排他的: 関数または class キーワードに、排他的エフェクトの指定子を 1 つだけ追加できます。または、まったく追加しないことも可能です。排他的エフェクトの指定子が追加されていない場合、デフォルトのエフェクトは no_rollback です。

  • 加法的: 関数または class キーワードに、加算エフェクトの指定子のすべてまたは一部を追加できます。または、まったく追加しないことも可能です。

画像をクリックすると拡大表示されます。

例エフェクトの内容

name() : type = codeblock

no_rollback: これは、排他的エフェクトが指定されていない場合のデフォルトのエフェクトです。no_rollback エフェクトは、関数によって実行されたすべてのアクションを元に戻すことができないため、関数を失敗コンテキストで使用できないことを示します。このエフェクトは手動では指定できません。

排他的エフェクト

name()<transacts> : type = codeblock

transacts: このエフェクトは、関数によって実行されるすべてのアクションをロールバックできることを示します。変更可能な変数 (var) が書き込まれる際は、常に transacts エフェクトが必要となります。ロールバックできない関数に transacts エフェクトが追加された場合、コードをコンパイルするときにそれが通知されます。これは、native 指定子を持つ関数に対しては行われないことに注意してください。

name()<varies> : type = codeblock

varies: このエフェクトは、関数に対する同じ入力が必ずしも同じ出力を生成するとは限らないことを示します。varies エフェクトは、関数の動作がそれを含むパッケージの新しいバージョンで同じになることが保証されないことを示します。

name()<computes> : type = codeblock

computes: このエフェクトには、副次的エフェクトがない関数が必要です。また、関数の確実な完了を保証しません。この関数は、同じ引数が指定されている場合に同じ結果を返すという要件がありますが、これはコンパイラによってチェックされません。native 指定子を持たない関数の場合、そうなりません。computes エフェクトを使用する際の良い例は、converges エフェクトです。

name()<converges> : type = codeblock

converges: このエフェクトは、関連する関数の実行による副次的エフェクトがないだけでなく、関数が確実に完了する (無限に再帰しない) ことも保証します。このエフェクトは native 指定子を持つ関数でのみ表示されますが、これはコンパイラによってチェックされません。このエフェクトを保持するには、クラスのデフォルト値またはグローバル変数の値を提供するコードが必要となります。

加算エフェクト

name()<transacts><decides> : type = codeblock

decides: 関数が失敗する可能性があること、およびこの関数の呼び出しが 失敗する可能性がある式 であることを示します。decides エフェクトのある関数は transacts エフェクトも保持している必要があります。つまり、関数のどこかでエラーが発生した場合、この関数によって実行されるアクションを (アクションがまったく実行されなかったかのように) ロールバックできます。

name()<suspends> : type = codeblock

suspends: 関数が非同期であることを示します。関数本体の async コンテキストを作成します。

いずれの場合も、特定のエフェクトを持つ関数を呼び出すには、呼び出し元もそのエフェクトを保持している必要があります。

エフェクト指定子について

エフェクト指定子の2つの大分類

1. 排他的エフェクト(1つしか使えない)

  1. Converges(収束)

    • 必ず終了することを保証

    • 副作用なし

    • 数学的な関数のような動作

  2. Computes(計算)

    • 終了保証なし

    • 同じ入力→同じ出力

    • 副作用なし

  3. Varies(可変)

    • 同じ入力でも結果が変わる可能性

    • 非決定的な動作を示す

  4. Transacts(トランザクション)

    • 失敗時に操作を取り消し可能

    • データ変更可能

    • 読み書き操作を含む

  5. No Rollback(デフォルト)

    • 操作の取り消し不可

    • デフォルトの動作

    • 明示的な指定は不要

2. 加法的エフェクト(組み合わせ可能)

  1. Suspends(一時停止)

    • 非同期関数であることを示す

    • 完了までに時間がかかる可能性

    • async contextを作成

  2. Decides(決定)

    • 失敗する可能性がある関数

    • Transactsと組み合わせ必須

    • エラー処理が可能

実践的な使用例

# トランザクションと決定を組み合わせた例
UpdatePlayerScore<transacts><decides>(): bool {
    if (IsValidScore()) {
        # スコア更新処理
        return true
    }
    return false
}

# 非同期処理の例
LoadResources<suspends>(): void {
    # リソースの非同期読み込み
}

# 計算処理の例
CalculateDistance<computes>(): float {
    # 距離計算
    return result
}

エフェクトの動作特性

  • DIVERGESは制御フローの分岐

  • VARIESは結果の不確実性

  • READSはデータ読み取り

  • WRITESはデータ書き込み

  • NO_ROLLBACKは操作の不可逆性

これらのエフェクト指定子を適切に使用することで、関数の振る舞いを明確に定義し、安全で予測可能なコードを書くことができます。

エフェクト指定子の詳細解説(コード例付き)

1. 排他的エフェクト(Exclusive Effects)

Converges(収束)

# 必ず終了する数学的な計算
CalculateSquare<converges>(x: float): float = x * x

Computes(計算)

# 同じ入力で同じ結果を返す計算
CalculateDamage<computes>(baseAmount: float, multiplier: float): float {
    return baseAmount * multiplier
}

Varies(可変)

# 実行毎に結果が変わる可能性がある
GetRandomNumber<varies>(): int {
    # ランダム値を生成
    return RandomInt(1, 100)
}

Transacts(トランザクション)

# データの変更を含む処理
UpdatePlayerHealth<transacts>(amount: float): void {
    Player.Health += amount
    if (Player.Health < 0.0) {
        Player.Health = 0.0
    }
}

No Rollback(デフォルト)

# デフォルトの動作(明示的な指定は不要)
DisplayMessage(): void {
    Print("このメッセージは取り消せません")
}

2. 加法的エフェクト(Additive Effects)

Suspends(非同期)

# 非同期でリソースを読み込む
LoadGameData<suspends>(): []resource {
    # 非同期データ読み込み処理
    Sleep(2.0)  # 仮の待機時間
    return GetResources()
}

Decides(決定)とTransactsの組み合わせ

# 失敗する可能性のある処理
BuyItem<transacts><decides>(itemId: string): bool {
    if (Player.Gold >= ItemCost) {
        Player.Gold -= ItemCost
        Player.Inventory.AddItem(itemId)
        return true
    }
    return false
}

複合的な使用例

# 複数のエフェクトを組み合わせた例
@component
GameShop := class:
    # アイテム購入処理
    PurchaseItem<public><transacts><decides><suspends>(
        itemId: string
    ): bool {
        # 非同期でアイテムデータを読み込み
        var itemData := LoadItemData(itemId)
        
        # 購入処理
        if (CanPurchase(itemData)) {
            UpdatePlayerInventory(itemData)
            return true
        }
        return false
    }
    
    # ランダムアイテムの生成
    GenerateRandomItem<varies>(): item_data {
        # ランダムアイテムを生成
        return CreateRandomItem()
    }

エフェクト指定子の特徴まとめ

  1. 排他的エフェクト

    • 一つの関数に1つだけ指定可能

    • 関数の基本的な動作特性を定義

    • デフォルトはno_rollback

  2. 加法的エフェクト

    • 他のエフェクトと組み合わせ可能

    • 特別な動作や状態を追加

    • 必要に応じて複数指定可能

これらのエフェクト指定子を適切に使用することで、関数の振る舞いを明確に定義し、安全で予測可能なコードを作成することができます。

no_rollbackエフェクトについて🎨

基本的な概念

no_rollbackは「取り消しができない処理」を意味します。

例え話で理解する

🎨 絵を描くことに例えると...

  • 鉛筆で描く = rollback可能(消しゴムで消せる)

  • ペンで描く = no_rollback(一度描いたら消せない)

コード例と解説

# これはデフォルトでno_rollbackになる
DisplayMessage(): void {
    Print("こんにちは!")  # 一度表示したメッセージは取り消せない
}

# ログを記録する関数
LogGameEvent(): void {
    # ログファイルに書き込み
    WriteLog("プレイヤーがログインしました")
    # 一度書き込んだログは取り消せない
}

重要なポイント

  1. デフォルトの動作

    • 特に指定しない場合は自動的にno_rollback

    • 明示的に指定する必要なし

  2. 取り消し不可

    • 実行した処理を元に戻せない

    • エラーが起きても取り消しができない

  3. 使用場面

    • ログ出力

    • メッセージ表示

    • デバッグ情報の出力

注意点

  • エラーが起きた時の対策が必要

  • 重要なデータ処理には使用を避ける

  • 取り消しが必要な場合は`<transacts>`を使用する

まとめ

no_rollbackは「一度やったら取り消せない処理」を表す基本的なエフェクトです。特に指定しなくても自動的に適用される「デフォルト」の動作なので、取り消しが必要ない単純な処理に適しています。

transacts(処理)エフェクトについて🎮

🎯 基本概念:「やり直しができる処理」

💡 例え話で理解する

  • ゲームのセーブ機能のようなもの

    • セーブポイントから戻れる = transacts

    • セーブなしで進む = no_rollback

🔄 具体的な例

# プレイヤーの所持金を更新する関数
UpdatePlayerGold<transacts>(amount: int): void {
    Player.Gold += amount
    
    # もし所持金がマイナスになったら
    if (Player.Gold < 0) {
        # 処理を取り消して元に戻す(ロールバック)
        fail()  # 失敗を示す
    }
}

✨ 主な特徴

  1. 安全な変数の更新

    • データの変更を安全に行える

    • エラー時に元の状態に戻せる

  2. 必須の場面

    • 変更可能な変数(var)を書き換える時

    • データベースの更新

    • ゲーム内アイテムの売買

🎮 ゲームでの具体例

# アイテム購入の処理
BuyItem<transacts>(itemId: string): void {
    # 所持金チェック
    if (Player.Gold < ItemPrice) {
        fail()  # 所持金不足でロールバック
    }
    
    # 購入処理
    Player.Gold -= ItemPrice
    Player.Inventory.AddItem(itemId)
}

⚠️ 注意点

  1. 使用すべき場面

    • データを変更する処理

    • エラーが起きる可能性がある処理

    • 安全性が必要な処理

  2. 使用を避けるべき場面

    • ログ出力のような取り消し不要な処理

    • 単純な表示処理

🎯 まとめ

transactsは「失敗したら元に戻せる」機能を提供します。

  • ✅ データの変更を安全に行える

  • ✅ エラー時に自動で元に戻る

  • ✅ 変更可能な変数の更新に必須

ゲームのセーブポイントのように、何か問題が起きたら安全に元の状態に戻れる機能だと覚えておくと良いでしょう!

Varies(可変)エフェクトについて🎲

基本的な概念

Variesは「変化する・異なる」という意味で、同じ入力でも異なる結果が返ってくる可能性がある関数に使用します。

分かりやすい例え話

🎲 ガチャガチャに例えると...

  • 同じ500円を入れても(同じ入力)

  • 毎回違うおもちゃが出てくる(異なる出力)

実践的なコード例

# ランダムなアイテムを出すガチャ関数
GetRandomItem<varies>(): item_type {
    # 毎回異なるアイテムを返す可能性がある
    return RandomSelectItem()
}

# 敵のランダム出現位置を決める関数
GetEnemySpawnLocation<varies>(): vector3 {
    # 毎回異なる位置を返す
    return RandomLocation()
}

主な使用場面

  1. ランダム要素が必要な処理

    • アイテムのドロップ

    • 敵の出現

    • ダメージのばらつき

  2. 時間依存の処理

    • 現在時刻を使用する計算

    • プレイヤーの入力に依存する処理

重要なポイント

  1. 予測不可能性

    • 同じ条件でも結果が変わる可能性

    • テストが複雑になる場合がある

  2. バージョン間の違い

    • 新しいバージョンで動作が変わる可能性

    • 完全な互換性は保証されない

使用時の注意点

  • デバッグが難しくなる可能性

  • テストケースの作成に注意が必要

  • ランダム性が必要な場合のみ使用

Variesエフェクトは、ゲームに予測不可能性や変化を加えたい場合に使用する重要な機能です!

computes(計算)エフェクトについて 🔢

基本的な概念

computesは「計算する」という意味で、純粋な計算処理を行う関数に使用します。

分かりやすい例え話

🧮 電卓に例えると...

  • 2 + 2 を計算すると(同じ入力)

  • 必ず4が出る(同じ出力)

  • 他の機能に影響を与えない(副作用なし)

実践的なコード例

# ダメージ計算(純粋な計算のみ)
CalculateDamage<computes>(baseDamage: float, multiplier: float): float {
    return baseDamage * multiplier
}

# 距離計算
CalculateDistance<computes>(point1: vector3, point2: vector3): float {
    # 2点間の距離を計算
    return (point2 - point1).Length()
}

主な特徴

  1. 純粋な計算

    • 入力値のみに基づいて計算

    • 外部の状態を変更しない

    • 同じ入力→同じ出力

  2. 副作用がない

    • 他の変数や状態を変更しない

    • データの保存やログ出力なし

    • 純粋に計算のみを行う

使用場面の例

  • 数学的な計算

  • スコアの計算

  • 座標の計算

  • 統計値の計算

注意点

  1. 完了保証なし

    • 無限ループの可能性あり

    • 処理が終わらない可能性も

  2. 制限事項

    • 変数の変更不可

    • ファイル操作不可

    • ネットワーク通信不可

まとめ

computesエフェクトは、純粋な計算処理を行う関数のために使用します。

  • ✅ 同じ入力なら同じ結果

  • ✅ 副作用なし

  • ✅ 純粋な計算処理に最適

計算機のような、入力に対して確実に同じ結果を返す処理を作る時に使用すると良いでしょう!

converges(収束)エフェクトについて 🔄

基本的な概念

convergesは「収束する」という意味で、必ず終了することが保証された安全な関数であることを示します。

分かりやすい例え話

📱 電卓の四則演算に例えると...

  • 1 + 1 を計算すると(入力)

  • 必ず2という結果が出る(確実な出力)

  • 必ず計算が終わる(確実な終了)

  • 他の機能に影響を与えない(副作用なし)

実践的なコード例

# 単純な数学計算(必ず終了する)
CalculateSquare<converges>(x: float): float {
    return x * x
}

# 距離計算(必ず結果が返る)
GetDistance<converges>(point1: vector3, point2: vector3): float {
    return (point2 - point1).Length()
}

主な特徴

  1. 確実な終了

    • 必ず処理が完了する

    • 無限ループが発生しない

    • 結果が必ず返される

  2. 副作用なし

    • 他の変数や状態を変更しない

    • 純粋な計算処理のみ

    • 予測可能な動作

  3. 安全性の保証

    • 信頼性の高い処理

    • デバッグが容易

    • テストが簡単

使用場面の例

  • 数学的な計算

  • 座標変換

  • データの検証

  • 単純な値の変換

注意点

  1. native指定子との関係

    • ネイティブ関数でのみ使用可能

    • コンパイラによる自動チェックなし

  2. 実装時の制約

    • 再帰処理は注意が必要

    • 複雑な処理は避ける

    • シンプルな処理に限定

まとめ

convergesエフェクトは、最も安全で信頼性の高い関数を示します:

  • ✅ 必ず終了する

  • ✅ 副作用がない

  • ✅ 予測可能な結果

  • ✅ デバッグが容易

数学の公式のように、入力に対して必ず結果が得られ、かつ他に影響を与えない処理を作る時に使用すると良いでしょう!

computesとconvergesの使い分けについて 🔄

🎯 基本的な違い

computesの特徴

  • 計算は必ず同じ結果を返す

  • 終了は保証されない(無限ループの可能性あり)

  • 副作用なし

convergesの特徴

  • 計算は必ず同じ結果を返す

  • 必ず終了する(無限ループなし)

  • 副作用なし

📝 具体例で理解する

computesを使う場面

# 再帰的なフィボナッチ数列の計算
CalculateFibonacci<computes>(n: int): int {
    if (n <= 1) { return n }
    return CalculateFibonacci(n - 1) + CalculateFibonacci(n - 2)
    # 大きな数値で無限ループの可能性あり
}

# 複雑な数学計算
CalculateComplexFormula<computes>(x: float): float {
    # 複雑な計算処理
    # 場合によっては終了しない可能性あり
}

convergesを使う場面

# 単純な数学計算(必ず終了する)
CalculateSquare<converges>(x: float): float {
    return x * x  # 必ず終了する単純な計算
}

# 配列の要素数を返す
GetArrayLength<converges>(array: []int): int {
    return array.Length  # 必ず結果が返る
}

🎮 ゲーム開発での使用例

computes

  • 複雑なダメージ計算

  • パスファインディング計算

  • AI行動の計算

converges

  • 座標変換

  • 距離計算

  • スコア計算

  • 単純な統計値の計算

💡 なぜ両方必要なのか?

  1. 処理の複雑さによる使い分け

    • 単純な計算 → converges

    • 複雑な計算 → computes

  2. パフォーマンスの考慮

    • 高速で確実な処理 → converges

    • 複雑だが柔軟な処理 → computes

  3. 安全性の重要度

    • 絶対的な安全性が必要 → converges

    • 多少のリスクを許容 → computes

⚠️ 注意点

computesを使う時

  • 無限ループの可能性を考慮

  • 複雑な計算の場合に使用

  • テストを十分に行う

convergesを使う時

  • シンプルな処理に限定

  • 必ず終了する処理のみ

  • ネイティブ関数との連携時

🎯 まとめ

computesとconvergesは、計算の複雑さと安全性のバランスによって使い分けます:

  • computes: より複雑な計算に対応できるが、終了を保証しない

  • converges: シンプルだが、確実に終了する安全な計算用

初心者の方は、まずconvergesで安全な処理を書き、必要に応じてcomputesを使うようにすると良いでしょう!

加算エフェクト

decides(決定)エフェクトについて 🎮

基本的な概念

decidesは「決定する」という意味で、処理が失敗する可能性がある関数に使用します。必ずtransactsと一緒に使う必要があります。

分かりやすい例え話

🎮 ゲームでアイテムを購入する時に例えると...

  • お金が足りる → 購入成功(true)

  • お金が足りない → 購入失敗(false)+ 処理を取り消し

実践的なコード例

# アイテム購入の処理
BuyItem<public><transacts><decides>(itemId: string): bool {
    # お金が足りるかチェック
    if (Player.Gold < ItemPrice) {
        return false  # 購入失敗
    }
    
    # 購入処理
    Player.Gold -= ItemPrice
    Player.Inventory.AddItem(itemId)
    return true  # 購入成功
}

# レベルアップの処理
LevelUp<public><transacts><decides>(): bool {
    if (Player.Experience < RequiredExp) {
        return false  # 経験値不足
    }
    
    # レベルアップ処理
    Player.Level += 1
    Player.Experience -= RequiredExp
    return true  # 成功
}

主な特徴

  1. 失敗の可能性

    • 処理が成功するか失敗するかを明示

    • エラー発生時は安全に元に戻せる

  2. transactsとの組み合わせ

    • 必ずtransactsと一緒に使用

    • 失敗時に変更を取り消し可能

使用場面の例

  • アイテムの購入処理

  • キャラクターのレベルアップ

  • インベントリの操作

  • データの検証が必要な処理

注意点

  1. 必ずtransactsと使う

    • 単独では使用できない

    • 変更可能な処理に使用

  2. 戻り値の扱い

    • 成功:true

    • 失敗:false

まとめ

decidesエフェクトは、処理が失敗する可能性がある関数で使用し、失敗時に安全に元の状態に戻すことができる機能です。

  • ✅ 失敗の可能性を明示

  • ✅ 安全な処理の取り消し

  • ✅ エラーハンドリングが容易

ゲームの重要な処理で、失敗する可能性があり、かつ失敗時に安全に元に戻したい場合に使用すると良いでしょう!

suspends(一時停止)エフェクトについて ⏳

基本的な概念

suspendsは「一時停止する」という意味で、処理に時間がかかる可能性がある非同期関数に使用します。

分かりやすい例え話

🎮 ゲームのロード画面に例えると...

  • ゲームを起動する

  • ロード画面が表示される(一時停止)

  • データの読み込みが完了

  • ゲーム開始

実践的なコード例

# リソースの読み込み
LoadResources<suspends>(): void {
    # 重いデータの読み込み
    Sleep(2.0)  # 2秒待機
    LoadGameData()
}

# プレイヤーデータの保存
SavePlayerData<suspends>(): void {
    # データの保存処理
    # 時間のかかる処理
    SaveToDatabase()
}

主な使用場面

  1. データの読み込み

    • ゲームリソース

    • テクスチャ

    • サウンド

    • マップデータ

  2. ネットワーク通信

    • サーバーとの通信

    • オンラインデータの取得

    • マルチプレイヤー同期

  3. 重い処理

    • 大量のデータ処理

    • 複雑な計算

    • ファイル操作

特徴と利点

  1. 非同期処理

    • メインの処理をブロックしない

    • ゲームの動作が止まらない

    • スムーズな体験を提供

  2. 処理の制御

    • 処理の進捗状況を確認可能

    • エラーハンドリングが容易

    • ユーザーへのフィードバック可能

使用例

# ゲームのロード処理
LoadGame<suspends>(): void {
    # ロード画面表示
    ShowLoadingScreen()
    
    # データ読み込み
    LoadPlayerData()
    LoadWorldData()
    LoadAssets()
    
    # ロード画面を閉じる
    HideLoadingScreen()
}

まとめ

suspendsエフェクトは、時間のかかる処理を他の処理をブロックせずに実行するために使用します。

  • ✅ 重い処理を非同期で実行

  • ✅ ゲームの動作を妨げない

  • ✅ ユーザー体験を向上

ゲームの読み込みやセーブなど、時間のかかる処理を実装する際に使用すると良いでしょう!

非同期処理について ⏰

同期処理と非同期処理の違いを例で理解

🍜 ラーメン屋さんで例えると...

同期処理(順番待ち)の場合:

  1. お客さんAがラーメンを注文

  2. 調理完了まで待つ

  3. お客さんAがラーメンを受け取る

  4. 次のお客さんBが注文可能

非同期処理の場合:

  1. お客さんAがラーメンを注文

  2. 店員は注文を受けて調理開始

  3. その間にお客さんBやCの注文も受付可能

  4. 出来上がった順にラーメンを提供

🎮 ゲームでの具体例

# 同期処理の例(よくない例)
LoadGame(): void {
    LoadPlayerData()     # データ読み込み完了まで待つ
    LoadWorldData()      # 完了まで待つ
    LoadTextures()       # 完了まで待つ
    # 全部終わるまでゲームが固まる!
}

# 非同期処理の例(良い例)
LoadGame<suspends>(): void {
    ShowLoadingScreen()  # ロード画面表示
    
    # 並行して読み込み
    LoadPlayerData()     # データ読み込み開始
    LoadWorldData()      # 世界データ読み込み開始
    LoadTextures()       # テクスチャ読み込み開始
    
    # ロード中でもUIは動く
    UpdateLoadingProgress()
}

非同期処理のメリット

  1. 待ち時間の有効活用

    • 重い処理の完了を待つ間も他の処理が可能

    • ユーザー体験の向上

    • システムリソースの効率的な使用

  2. スムーズな動作

    • ゲームが固まらない

    • レスポンシブな操作感

    • 快適なプレイ体験

  3. 並行処理

    • 複数の処理を同時に実行可能

    • 効率的なリソース利用

    • 処理時間の短縮

まとめ

非同期処理とは、時間のかかる処理を「待つ必要なく」次の処理に進めることができる仕組みです。

  • ✅ ゲームが固まらない

  • ✅ 効率的な処理が可能

  • ✅ より良いユーザー体験を提供

レストランのように「注文を受けながら調理も同時に進める」という考え方で理解すると分かりやすいですね!

理解3

アクセス指定子

アクセス指定子は、メンバーと何でどのような方法でやり取りできるのかを定義します。アクセス指定子は、次のものに適用できます。

  • メンバーの識別子:name<specifier> : type = value

  • メンバーのキーワード var: var<specifier> name : type = value

name<public> : type = value

Public: この識別子には、どれでもアクセスできます。この指定子は以下で使用できます。

  • モジュール (module)

  • クラス (class)

  • インターフェース (interface)

  • 構造体 (struct)

  • 列挙型 (enum)

  • メソッド (method)

  • データ (data)

name<protected> : type = value

Protected: この識別子には、現在のクラスとサブタイプのみがアクセスできます。この指定子は以下で使用できます。

  • クラス (class)

  • インターフェース (interface)

  • 構造体 (struct)

  • 列挙型 (enum)

  • 非モジュール メソッド (non-module method)

  • データ (data)

name<private> : type = value

Private: この識別子は、現在のスコープ、つまり、この識別子を囲んでいるスコープ (モジュール、クラス、構造体など) 内でのみアクセスできます。この指定子は以下で使用できます。

  • クラス (class)

  • インターフェース (interface)

  • 構造体 (struct)

  • 列挙型 (enum)

  • 非モジュール メソッド (non-module method)

  • データ (data)

name<internal> : type = value

Internal: この識別子は、現在のすぐ外側のモジュールでのみアクセスできます。これがデフォルトのアクセス レベルです。この指定子は以下で使用できます。

  • モジュール (module)

  • クラス (class)

  • インターフェース (interface)

  • 構造体 (struct)

  • 列挙型 (enum)

  • メソッド (method)

  • データ (data)

アクセス指定子について 🔒

アクセス指定子とは?

プログラムの各部分に対する「アクセス権限」を設定するための仕組みです。

4つのアクセスレベル

1. `<public>` (誰でもアクセス可能) 🌐

# どこからでもアクセスできる
class<public> PlayerStats:
    var<public> Health: float = 100.0
  • 全ての場所からアクセス可能

  • APIとして公開したい機能に使用

2. `<protected>` (継承先までアクセス可能) 🛡️

# 自分のクラスと継承したクラスからアクセス可能
class BaseCharacter:
    var<protected> Speed: float = 5.0
  • 現在のクラスとその子クラスからアクセス可能

  • 共通機能を継承で使いたい場合に使用

3. `<private>` (自分だけアクセス可能) 🔒

# 同じクラス内でのみアクセス可能
class Player:
    var<private> SecretCode: string = "abc123"
  • 現在のスコープ内でのみアクセス可能

  • 内部でのみ使用する機能に使用

4. `<internal>` (同じモジュール内でアクセス可能) 🏠

# 同じモジュール内でアクセス可能
class<internal> GameUtils:
    var<internal> DefaultSettings: settings = {...}
  • 現在のモジュール内でのみアクセス可能

  • デフォルトのアクセスレベル

わかりやすい例え話 🏰

アクセス指定子は「城」に例えると分かりやすいです:

  • `<public>`: 誰でも入れる公園

  • `<protected>`: 家族と親戚だけが入れる家

  • `<private>`: 自分の部屋(他人は入れない)

  • `<internal>`: マンションの共用スペース(住人だけが使える)

使い分けのポイント 🎯

  1. 公開する機能

    • APIとして外部に公開 → `<public>`

    • モジュール内で共有 → `<internal>`

  2. 保護したい機能

    • クラス内部のみ → `<private>`

    • 継承先でも使用 → `<protected>`

まとめ 📝

アクセス指定子は、コードの「セキュリティレベル」を設定する機能です:

  • ✅ コードの安全性を高める

  • ✅ 意図しないアクセスを防ぐ

  • ✅ 適切な範囲で機能を共有できる

初心者の方は、まず`<public>`と`<private>`の使い分けから始めると良いでしょう!

理解4

クラス指定子

クラス指定子は、クラスのサブクラスを作成できるかどうかなど、クラスまたはそのメンバーの特定の特性を定義します。

pet := class<abstract>():
    Speak() : void

cat := class(pet):
    Speak() : void = {}

abstract: クラスまたはクラス メソッドに abstract 指定子が含まれている場合、そのクラスのインスタンスを作成することはできません。abstract クラスは、部分的な実装を含むスーパークラス、または共通のインターフェースとして使用されます。これは、スーパークラスのインスタンスを持つ必要がないものの、同様のクラスでプロパティや動作を重複させたくない場合に役立ちます。

cat := class<concrete>():
     # クラスが具象なのでフィールドを初期化する必要があります
    Name : string = "Cat"

concrete: クラスに concrete 指定子があるとき、空のアーキタイプで作成できる必要があります。つまり、すべてのクラスのフィールドはデフォルト値を保持する必要があります。concrete クラスのすべてのサブクラスは暗黙的に具象となります。抽象クラスから具象クラスを直接継承できるのは、両方のクラスが同じモジュールで定義されている場合のみです。

unique_class := class<unique>:
    Field : int

Main()<decides> : void =
    X := unique_class{Field := 1}
    X = X # X はそれ自体に等しいです
    Y := unique_class{Field := 1}
    X <> Y # X と Y は一意であるため等しくありません

unique: unique 指定子は、クラスに適用して一意のクラスにすることができます。unique クラスのインスタンスを作成するために、Verse は結果のインスタンスに一意の ID を割り当てます。これにより、unique クラスのインスタンスは、その ID を比較するだけで等しいかどうかを判断できます。unique 指定子がないクラスは、そのような ID がなく、等しいかどうかを判断するにはそのフィールドの値を比較する必要があります。つまり、unique クラスは = および <> 演算子で比較でき、比較可能な型のサブタイプです。

cat := class<final>():

final: final 指定子は、クラスとクラスのメンバーでのみ使用できます。

  • クラスに final 指定子がある場合、そのクラスのサブクラスを作成することはできません。

  • フィールドに final 指定子がある場合、サブクラスのフィールドをオーバーライドすることはできません。

  • メソッドに final 指定子がある場合、サブクラスのメソッドをオーバーライドすることはできません。

pets := module:
    cat<public> := class<public>:
        Sound<public> : string = "Meow"

MakeCat():void =
    # cat がモジュールの外で正常に構築されています
    MyNewCat := pets.cat{}

public: クラスに public 指定子がある場合、そのクラスが定義されたモジュールの外であっても、誰でもそのクラスのインスタンスを構築することができます。

pets := module:
    cat<public> := class<internal>:
        Sound<public> : string = "Meow"

GetCatSound(InCat:pets.cat):string =
    return InCat.Sound # 有効:cat クラスを参照するものの、そのコンストラクタは呼び出しません

MakeCat():void =
    MyNewCat := pets.cat{} # エラー:内部クラスのコンストラクタへのアクセスが無効です

internal: クラスが internal 指定子を持つ場合、そのコンストラクタは同じモジュールまたはサブモジュール内でのみ呼び出すことができます。

クラス指定子について 🎮

5つの主要なクラス指定子

1. `<abstract>` (抽象クラス) 📝

pet := class<abstract>():
    Speak() : void  # 抽象メソッド
  • 直接インスタンス化できない設計図のようなもの

  • 継承して使うための基本クラス

  • 共通の機能をまとめるのに便利

2. `<concrete>` (具象クラス) 🏗️

cat := class<concrete>():
    Name : string = "Cat"  # 初期値必須
  • 実際に使用できるクラス

  • すべてのフィールドに初期値が必要

  • 直接インスタンス化可能

3. `<unique>` (一意クラス) 🎯

PlayerID := class<unique>:
    ID : int
  • 各インスタンスが一意のIDを持つ

  • 同じ値でも別のインスタンスは別物として扱われる

  • 比較演算子(=, <>)で直接比較可能

4. `<final>` (最終クラス) 🔒

Dog := class<final>():
    Bark() : void = {}
  • 継承できないクラス

  • これ以上の機能追加や変更を防ぐ

  • 安全性を高めたい場合に使用

5. `<public>`/`<internal>` (アクセス制御) 🌐

# 公開クラス
Animal<public> := class:
    Name : string = "Unknown"

# 内部クラス
Helper<internal> := class:
    Utility() : void = {}
  • `<public>`: 誰でもアクセス可能

  • `<internal>`: 同じモジュール内でのみアクセス可能

まとめ 📚

クラス指定子は、クラスの性質を定義する重要な機能です:

  • ✅ クラスの使用方法を制御

  • ✅ コードの安全性を向上

  • ✅ 設計の意図を明確に表現

初心者の方は、まず`<public>`と`<internal>`の使い分けから始め、徐々に他の指定子も使っていくことをお勧めします!

<abstract>(抽象クラス)とは📝

設計図の設計図のようなものです。直接使うことはできませんが、他のクラスを作る時の基本となるものです。

具体例で理解する

🐾 ペットの例を使って説明します:

pet := class<abstract>():    # 抽象的な「ペット」クラス
    Speak() : void          # 話す機能(中身は未定義)

cat := class(pet):          # 具体的な「猫」クラス
    Speak() : void = {}     # 実際の話す機能を定義

わかりやすい例え

  • 抽象クラス(abstract) = レシピの基本形

    • 「料理を作る」という概念だけがある

    • 実際には作れない

  • 具体的なクラス = 実際のレシピ

    • 「カレーを作る」という具体的な手順がある

    • 実際に作れる

なぜ使うの?

  1. 共通のルール作り

    • 似たような機能を持つクラスの基本ルールを決められる

    • 例:すべてのペットは「話す」機能を持つべき

  2. コードの整理

    • 似たような機能を何度も書かなくて済む

    • 変更が必要な時も一箇所で済む

  3. 安全性の確保

    • 不完全なクラスが使われるのを防ぐ

    • 必要な機能の実装し忘れを防ぐ

まとめ

abstractは「これを元に具体的なものを作ってね」という設計図のようなものです。直接使うことはできませんが、似たようなクラスを作る時の土台として便利です。

初心者の方へのアドバイス:

  • まずは「設計図の設計図」というイメージを持つと理解しやすいです

  • 実際のプログラミングでは、共通の特徴を持つクラスを作る時によく使います

<concrete>`(具象クラス) とは🏗️

concreteとは?

「具体的な」という意味で、実際に使用できる完全なクラスを表します。

分かりやすい例え話 🏠

家の設計図に例えると:

  • 設計図(抽象クラス):基本的な設計だけで実際には建てられない

  • 実際の家(concrete クラス):すべての詳細が決まっていて実際に建てられる

重要なポイント

1. 初期値が必須 📝

# 正しい例
cat := class<concrete>():
    Name : string = "Cat"    # 初期値あり
    Age : int = 0           # 初期値あり

# 間違った例
cat := class<concrete>():
    Name : string           # エラー:初期値がない!

2. インスタンス化が可能 🎯

# concreteクラスは直接使える
MyCat := cat{}  # OK!新しい猫を作れる

3. 継承のルール 🔄

  • concreteクラスを継承したクラスも自動的にconcreteになる

  • 抽象クラスからconcreteクラスを作る場合は同じモジュール内である必要がある

まとめ 📌

concreteクラスは:

  • ✅ すべてのフィールドに初期値が必要

  • ✅ 実際に使用できるクラス

  • ✅ インスタンス化が可能

  • ✅ 継承時は特別なルールあり

初心者の方は、「実際に使える完成品のクラス」というイメージで覚えると良いでしょう!

<unique>`(一意クラス) とは🎯

uniqueとは?

それぞれのインスタンスに「固有のID」が自動的に付与される特別なクラスを作る機能です。

分かりやすい例え話 🎫

映画館のチケットに例えると:

  • 同じ映画、同じ時間、同じ座席の情報(Field値)でも

  • チケット番号(固有ID)が違えば別のチケット

コード例で理解する 📝

# ユニーククラスの定義
PlayerID := class<unique>:
    Score : int

# 使用例
Main():void =
    Player1 := PlayerID{Score := 100}
    Player2 := PlayerID{Score := 100}
    
    # 同じスコアでも別の選手
    if (Player1 <> Player2):  # true
        Print("違う選手です")

uniqueの特徴 🌟

  1. 自動ID付与

    • インスタンス作成時に自動でIDが付く

    • プログラマーはIDを意識する必要なし

  2. 比較の簡単さ

    • `=` で同一性チェック

    • `<>` で異なることをチェック

  3. 値ではなくIDで比較

    • 中身が同じでも別物として扱える

    • データベースの主キーのような働き

使用例 🎮

# プレイヤーの装備品
Equipment := class<unique>:
    Name : string
    Power : int

# 同じ名前と能力値でも別の装備品として扱える
Sword1 := Equipment{Name := "剣", Power := 10}
Sword2 := Equipment{Name := "剣", Power := 10}

# 別の装備品として認識される
if (Sword1 <> Sword2):
    Print("別の装備品です")

まとめ 📌

uniqueクラスは:

  • ✅ 自動的に固有IDが付く

  • ✅ 同じ値でも別物として扱える

  • ✅ 比較が簡単

  • ✅ インスタンスの一意性が保証される

初心者の方は、「同じ内容でも別物として扱いたい時に使う」というイメージで覚えると良いでしょう!

<final>`(最終クラス) とは🔒

finalとは?

「最終的な」という意味で、これ以上の変更や拡張を許可しないクラスを作る機能です。

分かりやすい例え話 📦

完成品の家電製品に例えると:

  • 製品は完成していて改造できない(継承不可)

  • 部品は固定されていて交換できない(フィールドのオーバーライド不可)

  • 機能は変更できない(メソッドのオーバーライド不可)

コード例で理解する 📝

# 完成品のクラス
Cat := class<final>():
    Name : string = "Tama"
    
    Meow(): void = {
        Print("にゃー")
    }

# 以下はエラーになる
# SubCat := class(Cat):  # Catクラスは継承できない!

finalの3つの使用場面 🎯

  1. クラス全体をfinalにする

Animal := class<final>():  # このクラスは継承できない
  1. フィールドをfinalにする

Animal := class:
    var<final> species: string  # この属性は子クラスで変更できない
  1. メソッドをfinalにする

Animal := class:
    Speak<final>(): void = {  # この関数は子クラスで上書きできない
        Print("...")
    }

まとめ 📌

finalは以下を防ぐための機能:

  • ✅ クラスの継承を防ぐ

  • ✅ フィールドの上書きを防ぐ

  • ✅ メソッドの上書きを防ぐ

初心者の方は、「これ以上変更できないようにロックをかける機能」というイメージで覚えると良いでしょう!

publicクラス指定子とは 🌐

publicとは?

「公開」という意味で、誰でもアクセスできるクラスを作る機能です。

分かりやすい例え話 🏪

公共の施設に例えると:

  • 公園(public):誰でも利用できる

  • 会員制施設(非public):メンバーだけが利用できる

コード例で解説 📝

# モジュール(パッケージ)の定義
pets := module:
    # 誰でも使えるクラス
    cat<public> := class<public>:
        Sound<public> : string = "Meow"

# 別のファイルやモジュールから使用
MakeCat(): void =
    MyNewCat := pets.cat{}  # OK! 外からでも猫を作れる

publicの特徴 🌟

  1. どこからでもアクセス可能

    • モジュールの外からでも使える

    • 他のプログラマーも使える

    • APIとして公開できる

  2. インスタンス化が自由

    • new演算子で自由にオブジェクトを作成可能

    • どこからでもオブジェクトを生成可能

  3. 使用例

    • 共通のデータ構造

    • ユーティリティクラス

    • 共有リソース

まとめ 📌

publicクラスは:

  • ✅ どこからでもアクセス可能

  • ✅ 自由にインスタンス化可能

  • ✅ 共有して使うためのクラス

  • ✅ APIとして公開するのに適している

初心者の方は、「誰でも使えるように公開された機能」というイメージで覚えると良いでしょう!

internalクラスとは 🏠

internalとは?

「内部限定」という意味で、同じモジュール(パッケージ)内でのみ新しいインスタンスを作れるクラスを定義します。

分かりやすい例え話 🏢

社内の施設に例えると:

  • 社員食堂(internal):同じ会社の人だけが利用できる

  • 参照(見ること)は外部の人もOK

  • 実際に利用(インスタンス作成)は社員のみ

コード例で解説 📝

pets := module:
    # クラスは参照可能だが、作成は内部のみ
    cat<public> := class<internal>:
        Sound<public> : string = "Meow"

# 外部からの使用
GetCatSound(InCat:pets.cat):string =
    return InCat.Sound  # OK! 参照は可能

MakeCat():void =
    MyNewCat := pets.cat{}  # エラー! 外部からは作成できない

internalの特徴 🌟

  1. 参照と作成の区別

    • クラスの参照:どこからでもOK

    • インスタンス作成:同じモジュール内のみ

  2. セキュリティ制御

    • オブジェクトの作成を制限

    • 内部実装の保護

    • 不正な使用を防止

まとめ 📌

internalクラスは:

  • ✅ 同じモジュール内でのみインスタンス作成可能

  • ✅ クラスの参照は外部からも可能

  • ✅ 内部実装を保護しつつ、機能は公開できる

初心者の方は、「見ることはできるけど、作れるのは仲間だけ」というイメージで覚えると良いでしょう!

実装指定子

コードを記述するときに実装指定子を使用することはできません。ただし、実装指定子は UEFN API で確認できます。

GetCreativeObjectsWithTag<native><public>(Tag:tag)<transacts>:[]creative_object_interface

native: 要素の定義の詳細が C++ で実装されていることを示します。native 指定子を持つ Verse 定義は、C++ 定義を自動生成します。Verse デベロッパーはその実装に入力できます。この指定子が次の場所で使用されていることを確認できます。

  • クラス (class)

  • インターフェース (interface)

  • 列挙型 (enum)

  • メソッド (method)

  • データ (data)

creative_device<native><public> := class<concrete>:
    OnBegin<public>()<suspends>:void = external {}

    OnEnd<native_callable><public>():void = external {}

native_callable: インスタンス メソッドがネイティブ (C++ で実装されている) であり、他の C++ コードによって呼び出される可能性があることを示します。この指定子がインスタンス メソッドで使用されていることがわかります。この指定子はサブクラスに伝播されません。したがって、この指定子を持つメソッドをオーバーライドする際、定義にこの指定子を追加する必要はありません。

実装指定子について 🔧

nativeとnative_callableの基本概念

1. `<native>` 指定子

  • C++で実装された機能であることを示す

  • UEFNが提供する機能を使用する時に見かける

  • 開発者は直接使用できない(APIでのみ確認可能)

# UEFNのAPIの例
GetCreativeObjectsWithTag<native><public>(Tag:tag)<transacts>:[]creative_object_interface

2. `<native_callable>` 指定子

  • C++から呼び出し可能なメソッドであることを示す

  • デバイスのイベントハンドラなどで使用

  • 継承時は指定子の追加は不要

# デバイスクラスの例
creative_device<native><public> := class<concrete>:
    OnBegin<public>()<suspends>:void = external {}
    OnEnd<native_callable><public>():void = external {}

分かりやすい例え話 🎮

nativeとnative_callableを「通訳」に例えると:

  • `<native>`: 英語(C++)で書かれた説明書

  • `<native_callable>`: 英語(C++)で話しかけることができる通訳

重要なポイント 📌

  1. 開発者は直接使用不可

    • APIを使用する時のみ確認できる

    • UEFNが提供する機能の一部

  2. 主な使用場面

    • UEFNのAPI機能

    • デバイスのイベント処理

    • エンジンとの連携機能

  3. 継承時の注意点

    • `<native_callable>`は子クラスに自動的に引き継がれない

    • オーバーライド時は指定不要

まとめ 🎯

実装指定子は:

  • ✅ UEFNのシステムとの橋渡し役

  • ✅ C++実装との連携を示す

  • ✅ APIでのみ確認可能

  • ✅ 直接使用はできない

初心者の方は、「UEFNが提供する機能を使うための特別なマーク」というイメージで覚えると良いでしょう!

実装指定子の基本 🐵

実装指定子とは? 🤔

実装指定子は、コードの特定の部分がどのように動作するかを示す「札」のようなものです。

主な実装指定子 📌

1. native指定子

class MyDevice<native><public> := class<concrete>
  • 🍌 サルの例:お弁当箱の中身は見えないけど、中に何が入っているか表示されているようなもの

  • 実際の中身はC++で書かれていて、Verseからは見えない

  • でも使うことはできる!

2. native_callable指定子

OnEnd<native_callable><public>():void = external {}
  • 🐒 サルの例:他の猿に「バナナあげるよ!」と声をかけられる状態

  • C++のコードから呼び出せるメソッドであることを示す

  • 子クラスには自動的に引き継がれない

重要なポイント 🌟

  1. 使用制限

  • 👉 普通のユーザーは実装指定子を自分で書くことはできない

  • 👉 APIの中で確認することはできる

  1. 主な使用場所

  • クラス

  • インターフェース

  • 列挙型

  • メソッド

  • データ

初心者向けのたとえ話 🎯

想像してみてください:

  • 🏪 コンビニのお弁当(native)

    • 中身は工場で作られている(C++で実装)

    • でも、お客さん(Verseプログラマー)は食べることはできる

  • 📞 電話の受付(native_callable)

    • 外から電話がかかってくる(C++から呼び出される)

    • でも、誰でも電話に出られるわけではない

まとめ 📝

  • 実装指定子は「このコードはこういう特徴があります」という表示

  • 自分で書くことはできないけど、APIで見ることはできる

  • 主にC++とVerseの橋渡しをする役割がある

これで実装指定子の基本が理解できたかな?🐵 難しそうに見えても、実際は「このコードはこういう特徴があるよ」という札みたいなものだと覚えておけばOKです!

理解5

属性

Verse の属性は、Verse 言語以外で使用される動作を記述します (Verse セマンティクスを記述する指定子とは異なる)。属性は、定義の前のコード行に追加できます。

属性構文には、@ の後にキーワードが続きます。

@editable
Platform : color_changing_tiles_device = color_changing_tiles_device{}

editable: このフィールドは、UEFN から直接変更できる公開されたプロパティであるため、値を変更するために Verse コードを変更する必要がないことを示します。詳細については、「Customize Device Properties (仕掛けのプロパティをカスタマイズする)」を参照してください。

属性(@editable)について 🎮

属性とは? 🤔

属性は、コードの特定の部分に「特別な性質」を付け加えるための目印です。

@editableについて詳しく解説 ✨

基本的な使い方

@editable
MyPlatform : color_changing_tiles_device = color_changing_tiles_device{}

わかりやすい例え話 📝

  • 🎮 ゲームの設定画面みたいなもの

    • 音量調整

    • 明るさ調整

    • 難易度設定

@editableのメリット

  1. 👉 コードを変更せずに値を変更できる

  2. 👉 UEFNのエディター画面から直接編集可能

  3. 👉 ゲームをテストしながら調整できる

実践的な使用例 🎯

よくある使用シーン

@editable  # エディターで編集可能!
JumpHeight : float = 500.0  # ジャンプの高さ

@editable  # エディターで編集可能!
PlayerSpeed : float = 300.0  # プレイヤーの速度

初心者向けの具体例 🌟

想像してみてください:

  • 📺 テレビのリモコン

    • チャンネルを変える

    • 音量を調整する

    • これらはコードを書き換えずにできる!

@editableを使うメリット 💡

  1. 簡単な調整

  • プログラミングの知識がなくても値を変更できる

  • すぐに結果を確認できる

  1. 時間の節約

  • コードの再コンパイルが不要

  • 素早くテストができる

使用時の注意点 ⚠️

  • 公開プロパティになるので、適切な値の範囲を考慮する

  • 必要な項目だけeditableにする

  • デバッグやテスト時に特に便利

まとめ 📌

@editableは:

  • UEFNエディターから直接値を変更できる

  • コードを書き換えずに調整可能

  • 開発効率を大幅に向上させる

これで属性、特に@editableについて理解できましたか? 😊
実際に使ってみると、ゲーム開発がとても楽になりますよ!