ホルマリンなしの実際の図をブロックします

ブロック図は、複雑なアルゴリズムを理解しやすく構造化されたアクションシーケンスに変えるのに役立つ視覚ツールです。プログラミングからビジネスプロセス管理まで、最も複雑なシステムの視覚化、分析、最適化のための普遍的な言語として機能します。

道路の代わりにロジックであり、都市の代わりにアクションがある地図を想像してください。これはブロック図であり、最も混乱するプロセスでナビゲーションのための不可欠なツールです。

例1:簡素化されたゲームの起動スキーム
仕事の原則を理解するために、簡単なゲームの起動スキームを提示しましょう。

このスキームは、すべてが失敗することなく起こるときの完璧なスクリプトを示しています。しかし、実際の生活では、すべてがはるかに複雑です。

例2:データ読み込みでゲームを開始するための拡張スキーム
最新のゲームでは、ユーザーデータ、保存、または設定をダウンロードするためにインターネット接続が必要になることがよくあります。これらの手順をスキームに追加しましょう。

このスキームはすでにより現実的ですが、何かがうまくいかない場合はどうなりますか?

どうでしたか:インターネットの損失で「壊れた」ゲーム

プロジェクトの開始時に、開発者は考えられるすべてのシナリオを考慮することができませんでした。たとえば、彼らはゲームの主な論理に焦点を合わせており、プレーヤーにインターネット接続がある場合に何が起こるか考えませんでした。

このような状況では、彼らのコードのブロック図は次のようになります。

この場合、エラーを発行したり正しく閉じたりする代わりに、接続がないために受け取っていないデータを待つ段階でゲームが凍結しました。これにより、「ブラックスクリーン」が発生し、アプリケーションが凍結されました。

それがどのようになったか:ユーザーの苦情の修正

ホバリングに関する多くのユーザーの苦情の後、開発者チームは、エラーを修正する必要があることに気付きました。彼らは、アプリケーションが接続の欠如に正しく応答できるエラー処理ユニットを追加することにより、コードを変更しました。

これは、修正されたブロック図がどのように見えるかであり、両方のシナリオが考慮されます。

このアプローチのおかげで、ゲームはユーザーに問題について正しく通知し、場合によってはオフラインモードに移動してゲームを続けることができます。これは、ブロック図が非常に重要である理由の良い例です:開発者に、実行の理想的な方法だけでなく、すべての可能な障害についても考えさせ、最終製品をより安定して信頼できるものにします。

不確実な行動

ハンギングとエラーは、プログラムの予測不可能な動作の例の1つにすぎません。プログラミングでは、不確実な動作(未定義の行動)の概念があります – これは、言語の基準が特定のケースでプログラムの動作を説明しない状況です。

これは何にもつながる可能性があります:撤退のランダムな「ごみ」からプログラムの失敗や深刻なセキュリティの脆弱性まで。たとえば、メモリを使用して作業するとき、無期限の動作はしばしば発生します。たとえば、Cの言語の線で

言語cの例:

開発者がラインをバッファーにコピーしたが、ゼロシンボル( `\ 0`)を最後に追加するのを忘れたと想像してください。

これがコードがどのように見えるかです:

#include 

int main() {
char buffer[5];
char* my_string = "hello";

memcpy(buffer, my_string, 5);

printf("%s\n", buffer);
return 0;
}

予想される結果:「こんにちは」
実際の結果は予測不可能です。

なぜこれが起こっているのですか? spitifier`%sを使用した「printf」関数は、線がゼロシンボルで終了することを期待しています。彼がそうでない場合、彼女はハイライトされたバッファーの外でメモリを読み続けます。

次に、このプロセスのブロック図を紹介します。

これは、ブロック図が非常に重要である理由の明確な例です。開発者に、理想的な実行方法だけでなく、そのような低レベルの問題を含むすべての可能な障害についても考えさせ、最終製品のより安定性と信頼性を高めます。

Pixel Perfect:宣言性の時代の神話または現実?

インターフェイス開発の世界では、共通の概念があります – 「ロッジに完璧なピクセル」。これは、デザインマシンの最小ピクセルへの最も正確な複製を意味します。長い間、それは、特に古典的なウェブデザインの時代において、ゴールドスタンダードでした。しかし、宣言的なマイルの到着とさまざまなデバイスの急速な成長により、「Pixel Perfect」の原則はより一時的になりつつあります。理由を把握してみましょう。

Imperial Wysiwyg vs.宣言コード:違いは何ですか?

従来、多くのインターフェイス、特にデスクトップは、編集者の命令的なアプローチまたはwysiwyg(あなたが見るものです)を使用して作成されていました。このようなツールでは、デザイナーまたは開発者は要素を直接操作し、ピクセルに正確にキャンバスに配置します。それはグラフィックエディターとの作業に似ています – あなたはあなたの要素がどのように見えるかを見ることができ、あなたは間違いなくそれを配置することができます。この場合、「Pixel Perfect」の達成は非常に現実的な目標でした。

ただし、現代の開発は、宣言マイルにますます基づいています。これは、コンピューターに「このボタンをここに置く」ように指示するのではなく、取得したいものを説明することを意味します。たとえば、要素の特定の座標を示す代わりに、そのプロパティについて説明します。「このボタンは赤く、すべての側面から16pxのインデントを持ち、容器の中心にある必要があります。」 Freimvorkiは、React、Vue、Swiftui、Jetpackのようなものです。この原則を使用するだけです。

なぜ「Pixel Perfect」が多くのデバイスで宣言的なマイルで動作しない

iPhone 15 Pro Max、Samsung Galaxy Fold、iPad Pro、および4K解像度で等しく見栄えの良いアプリケーションを作成すると想像してください。これらの各デバイスには、画面解像度、ピクセル密度、パーティ、および物理サイズが異なります。

宣言的アプローチを使用する場合、システム自体は、すべてのパラメーターを考慮して、特定のデバイスに説明されたインターフェイスを表示する方法を決定します。厳しい座標ではなく、ルールと依存関係を設定します。

* 適応性と応答性:宣言マイルの主な目標は、適応的で応答性のあるインターフェイスを作成することです。これは、読みやすさを壊して維持せずに、インターフェイスが画面のサイズと方向に自動的に適応する必要があることを意味します。各デバイスで「Pixel Perfect」を試みた場合、同じインターフェイスの無数のオプションを作成する必要があります。
* ピクセル密度(DPI/PPI):デバイスのピクセル密度は異なります。スケーリングを考慮しない場合、高密度のデバイス上の100の「仮想」ピクセルのピクセルの同じ要素は、低密度デバイスよりもはるかに小さく見えます。宣言的なフレームワークは、物理的なピクセルによって抽象化され、論理ユニットを使用します。
* 動的コンテンツ:最新のアプリケーションのコンテンツは、しばしば動的です – その体積と構造は異なる場合があります。ピクセルに激しくぼろぼろにした場合、テキストや画像の変更はレイアウトの「崩壊」につながります。
* さまざまなプラットフォーム:さまざまなデバイスに加えて、異なるオペレーティングシステム(iOS、Android、Web、デスクトップ)があります。各プラットフォームには、独自の設計、標準コントロール、フォントがあります。すべてのプラットフォームで完全に同一のピクセル完璧なインターフェイスを作成しようとすると、不自然なタイプとユーザーエクスペリエンスの低下につながります。

古いアプローチは消えませんでしたが、進化しました

インターフェイスへのアプローチは、「必須」と「宣言」の間のバイナリ選択ではないことを理解することが重要です。歴史的に、各プラットフォームには、インターフェイスの作成に対する独自のツールとアプローチがありました。

* ネイティブインターフェイスファイル: iOSの場合は、Android-XMLマーキングファイルのXIB/ストーリーボードでした。これらのファイルは、ピクセルに最適なWysiWygレイアウトであり、エディターと同様に無線に表示されます。このアプローチはどこにも消えておらず、開発を続けており、最新の宣言的なフレームと統合されています。たとえば、AppleとJetpackのSwiftuiはAndroidで構成され、純粋に宣言的なコードのパスで出発しましたが、同時に古典的なレイアウトを使用する機会を保持しました。
* ハイブリッドソリューション:多くの場合、実際のプロジェクトでは、アプローチの組み合わせが使用されます。たとえば、アプリケーションの基本構造は宣言的に実装できます。特定のために、要素の正確な位置決めを必要とするため、プラットフォームの詳細を考慮して、より低レベルの命令的な方法を使用するか、ネイティブコンポーネントを開発できます。

モノリスから適応性への:デバイスの進化が宣言的なマイルを形成する方法

デジタルインターフェイスの世界は、過去数十年にわたって大きな変化を遂げてきました。固定許可を持つ固定コンピューターから、さまざまなユーザーデバイスの指数関数的な成長の時代になりました。今日、当社のアプリケーションは次のことで等しくうまく機能するはずです

* すべてのフォームファクターと画面サイズのスマートフォン
* タブレット独自の方向モードと分離画面があります。
* ラップトップとデスクトップさまざまな許可証のモニター。
* テレビとメディアセンター、リモートで制御されています。テレビでさえ、最小限のボタンを備えたApple TVリモート
のように単純なものになる可能性があることは注目に値します。インターフェイスは、特定のリモートコントロールと対話する「方法」を追加することなく、「まるでそれ自体のように」機能するはずです。
* ミニマルな画面を備えたスマートウォッチとウェアラブルデバイス
* 仮想現実ヘルメット(VR)。空間インターフェイスに対するまったく新しいアプローチが必要です。
* 拡張現実デバイス(AR)、現実世界に関する情報を適用します。
* 自動車情報およびエンターテイメントシステム
*そして、家電製品:感覚スクリーンを備えた冷蔵庫と、スマートオーブンとスマートハウスのシステムへのインタラクティブなディスプレイを備えた洗濯機から。

これらの各デバイスには、物理的寸法、パーティー比、ピクセル密度、入力方法(タッチスクリーン、マウス、コントローラー、ジェスチャー、ボーカルコマンド)、および重要なことに、ユーザー環境の微妙さ。たとえば、VR Shleshには深い浸漬が必要であり、外出先でのスマートフォンの高速で直感的な作業が必要ですが、冷蔵庫のインターフェイスは簡単で大きくて、迅速なナビゲーションを使用する必要があります。

古典的なアプローチ:個々のインターフェイスをサポートする負担

デスクトップの優位性と最初のモバイルデバイスの時代において、通常のビジネスは、個々のインターフェイスファイルのの作成とサポートでした。

* iOS に基づく開発は、xcodeでストーリーボードまたはxibファイルを使用する必要があることが多く、Objective-cまたはswiftのコードを作成しました。
* android XMLマーキングファイルとJavaまたはKotlinのコードが作成されました。
* WebインターフェイスはHTML/CSS/JavaScriptをオンにしました。
* c ++アプリケーションの場合さまざまなデスクトッププラットフォームで、特定のフレームワークとツールが使用されました。
* Windows では、これらはMFC(Microsoft Foundationクラス)、手動描画要素を備えたWin32 API、またはダイアログウィンドウとコントロール要素のリソースファイルを使用していました。
*グラフィックインターフェイスを直接制御するためのココア(Objective-C/Swift)または古いカーボンAPIが macos で使用されました。
* linux/unixのようなシステムでは、GTK+やQTなどのライブラリがよく使用され、XMLのようなマーキングファイル(QTデザイナーの.UIファイルなど)または要素の直接ソフトウェアの作成を介して、インターフェイスを作成するためのウィジェットとメカニズムのセットを提供しました。

このアプローチにより、各プラットフォームを最大限に制御できるようになり、特定のすべての機能とネイティブ要素を考慮することができます。しかし、彼には大きな欠点がありました。努力の複製とサポートのための途方もないコスト。設計または機能のわずかな変更には、実際には独立したコードベースがいくつかの権利を導入する必要がありました。これは、開発者チームにとって本当の悪夢になり、新しい機能の出力を遅くし、エラーの可能性を高めました。

宣言マイル:多様性のための単一言語

宣言マイルが支配的なパラダイムとして登場したのは、この急速な複雑さに対応していました。 React、Vue、Swiftui、Jetpackのようなframwsはを構成します。

宣言的アプローチの主なアイデア:システムがすべての要素を描画する方法(命令)を「どのように」(命令したいか(宣言)」と説明する代わりに。インターフェイスのプロパティと条件を設定し、フレームワークは特定のデバイスに最適に表示する方法を決定します。

これは、次の重要な利点のおかげで可能になりました。

1。プラットフォームの詳細からの抽象化:宣言的なfraimvorkiは、各プラットフォームの低レベルの詳細を忘れるように特別に設計されています。開発者は、単一の転送されたコードを使用して、より高いレベルの抽象化でコンポーネントとその関係を説明します。
2。自動適応と応答性: freimvorki 自動スケーリング、要素のレイアウト、およびさまざまなサイズの画面、ピクセル密度、入力方法へのレイアウトと適応の変更について責任を負います。これは、FlexBoxやグリッドなどの柔軟なレイアウトシステムの使用、および「論理ピクセル」や「DP」に似た概念を使用して達成されます。
3。ユーザーエクスペリエンスの一貫性:外部の違いにもかかわらず、宣言的アプローチにより、デバイスのファミリー全体で行動と相互作用の単一のロジックを維持できます。これにより、テストプロセスが簡素化され、より予測可能なユーザーエクスペリエンスが提供されます。
4。開発とコスト削減の加速:多くのプラットフォームで作業できるのと同じコードを使用すると、開発とサポートの時間とコストによってが大幅に削減されます。チームは、同じインターフェイスを繰り返し書き直すことではなく、機能と設計に焦点を合わせることができます。
5。 Freimvorkiは新しいテクノロジーをサポートするために更新でき、すでに書かれたコードがこのサポートを受け取ることは比較的シームレスです。

結論

宣言的なマイルは、単なるファッションのトレンドではなく、モノのインターネット(IoT)やスマート世帯家電などのユーザーデバイスの急速な開発によって引き起こされる必要な進化ステップです。これにより、開発者と設計者は、各プラットフォームの無限の特定の実装にownれなくなることなく、複雑で適応的で均一なインターフェイスを作成できます。各ピクセルの命令制御から目的の状態の宣言的記述への遷移は、将来のインターフェイスの世界では、表示される画面に関係なく、柔軟で、転送され、直感的であるであるべきであるという認識です。

プログラマー、デザイナー、ユーザーは、この新しい世界で生きる方法を学ぶ必要があります。 特定のデバイスまたは解像度に合わせて設計されたPixel Perfectの追加の詳細は、開発とサポートのために不必要な時間コストにつながります。さらに、このような過酷なレイアウトは、限られた入力テレビ、VR、ARシフトなどの標準以外のインターフェイスを備えたデバイスや、将来の他のデバイスでも機能しない場合がありますが、今日でもわかりません。柔軟性と適応性 – これらは、現代世界で成功したインターフェイスを作成するための鍵です。

バイブコアトリック:なぜLLMが固体、乾燥、きれいに動作しないのか

CHATGPTなどの大規模な言語モデル(LLM)の開発により、ますます多くの開発者がそれらを使用してコードを生成し、設計アーキテクチャを生成し、統合を加速します。ただし、実用的なアプリケーションでは、それは顕著になります。建築の古典原則 – 固体、乾燥、清潔 – LLMコドゲンデーションの特性とうまくやっていません。

これは、原則が時代遅れであることを意味するものではありません – それどころか、それらは手動開発と完全に連携しています。しかし、LLMでは、アプローチを適応させる必要があります。

なぜLLMが建築原則に対処できないのか

カプセル化

インクシレーションでは、システムの一部間の境界、開発者の意図に関する知識を理解し、厳格なアクセス制限に従う必要があります。 LLMは、多くの場合、構造を簡素化したり、理由もなくフィールドを公開したり、実装を複製したりします。これにより、コードはエラーに対してより脆弱になり、アーキテクチャの境界に違反します。

要約とインターフェイス

抽象的な工場や戦略などの設計パターンには、システムの全体的な見方が必要であり、そのダイナミクスを理解する必要があります。モデルは、実装を保証せずに明確な目的なしでインターフェイスを作成したり、レイヤー間の接続に違反したりできます。結果は、過剰または非機能的アーキテクチャです。

dry(donolt繰り返し自分で繰り返します)

LLMは、繰り返しコードを最小限に抑えようとはしません – それどころか、一般的なロジックを作成するよりもブロックを複製する方が簡単です。リクエストに応じてリファクタリングを提供できますが、デフォルトでは、たとえ冗長性につながる場合でも、「自己適切な」フラグメントを生成する傾向があります。

クリーンアーキテクチャ

Cleanは、厳格な階層、フレームワークからの独立性、指向性の依存性、およびレイヤー間の最小のつながりを意味します。このような構造の生成には、システムのグローバルな理解が必要です。また、LLMは、建築の完全性ではなく、単語の確率のレベルで機能します。したがって、コードは混合され、依存の方向に違反し、レベルへの単純化された分割が違反されます。

LLMを使用するときにうまく機能する

乾燥する代わりに濡れています
WET(すべてを2回書く)アプローチは、LLMでの作業においてより実用的です。コードの複製は、保持のモデルからのコンテキストを必要としません。つまり、結果は予測可能であり、正しく修正しやすくなります。また、非自明なつながりやバグの可能性を減らします。

さらに、複製はモデルの短いメモリを補うのに役立ちます。いくつかの場所でロジックの特定の断片が見つかった場合、LLMはそれをさらなる生成で考慮に入れる可能性が高くなります。これにより、伴奏が簡素化され、「忘却」に対する抵抗が増加します。

カプセル化の代わりに単純な構造

複雑なカプセル化を回避し、コードの部分間のデータの直接送信に依存すると、生成とデバッグの両方を大幅に簡素化できます。これは、MVPの迅速な開発または作成で特に当てはまります。

簡略化されたアーキテクチャ

プロジェクトのシンプルでフラットな構造は、最小量の依存関係と抽象化を備えており、発電中はより安定した結果をもたらします。モデルはこのようなコードを簡単に適応させ、コンポーネント間の予想される接続に違反することが多い場合があります。

SDK統合 – 手動で信頼できる

ほとんどの言語モデルは、時代遅れのバージョンのドキュメントでトレーニングされています。したがって、SDKをインストールするための手順を生成する場合、エラーがよく表示されます。時代遅れのコマンド、無関係なパラメーター、またはアクセスできないリソースへのリンク。練習のショー:公式のドキュメントと手動チューニングを使用して、LLMを補助的な役割にします。たとえば、テンプレートコードや構成の適応を生成します。

なぜ原則がまだ機能しているのか – しかし、手動開発で

固体で乾燥した清潔から清潔からの困難は、LLMを介したコドヘゲエネーションに懸念していることを理解することが重要です。開発者がコードを手動で書き込むと、これらの原則は引き続き価値を示し続けます。つながりを低下させ、サポートを簡素化し、プロジェクトの読みやすさと柔軟性を高めます。

これは、人間の思考が一般化する傾向があるという事実によるものです。私たちはパターンを探しています。個々のエンティティに繰り返し論理を持ち込み、パターンを作成します。おそらく、この動作には進化的なルーツがあります。情報の量を減らすと、認知リソースが節約されます。

LLMは異なる行動をとる:彼らはデータの量から負荷を経験せず、貯蓄を努力しません。それどころか、複雑な抽象化を構築および維持するよりも、それらが重複した断片化された情報を使用して作業するのが簡単です。そのため、繰り返し構造と最小限のアーキテクチャの重大度を備えた、カプセル化なしでコードに対処する方が簡単です。

結論

大規模な言語モデルは、特に初期段階で、または補助コードを作成する際に、開発において有用なツールです。ただし、アプローチを適応させることが重要です。アーキテクチャを簡素化し、抽象化を制限し、複雑な依存関係を避け、SDKを構成する際にそれらに依存しないようにすることが重要です。

固体、乾燥、きれいの原則はまだ関連していますが、人の手に最善の効果をもたらします。 LLMで作業する場合、手動で簡単に完成できる信頼できる理解可能なコードを取得できる簡素化された実用的なスタイルを使用することが合理的です。そして、LLMが忘れる場所 – コードの複製は彼が覚えておくのに役立ちます。

Surreal Engine C++ を WebAssembly に移植

この投稿では、Surreal Engine ゲーム エンジンを WebAssembly に移植する方法について説明します。

シュール エンジン – Unreal Engine 1 の機能のほとんどを実装するゲーム エンジン、このエンジン上の有名なゲーム -アンリアル トーナメント 99、アンリアル、デウス エクス、アンダイング。これは、主にシングルスレッド実行環境で動作する古典的なエンジンを指します。

当初、私は、妥当な時間内に完了できないプロジェクトに挑戦して、Twitch フォロワーに私でも実行できないプロジェクトがあることを示すことを考えていました。最初のストリーム中に、Emscripten を使用して Surreal Engine C++ を WebAssembly に移植するタスクが実行可能であることに突然気づきました。

Surreal Engine Emscripten Demo

1 か月後には、WebAssembly でフォークとエンジンのアセンブリをデモンストレーションできるようになります。
https://demensdeum.com/demos/SurrealEngine/

オリジナルと同様に、コントロールはキーボードの矢印を使用して実行されます。次に、これをモバイル コントロール (タチ) に適応させ、Unreal Championship 99 レンダリングの正しいライティングやその他のグラフィック機能を追加する予定です。

どこから始めればよいですか?

最初に言いたいのは、Emscripten を使用すると、どのプロジェクトでも C++ から WebAssembly に移植できるということです。唯一の問題は、機能がどの程度完成するかということです。 Surreal Engine の場合は、ライブラリ ポートがすでに Emscripten で利用できるプロジェクトを選択してください。エンジンは SDL 2、OpenAL – ライブラリを使用します。どちらも Emscripten に移植されています。ただし、Vulkan はグラフィックス API として使用されており、現在 HTML5 では利用できません。WebGPU の実装作業が進行中ですが、これもドラフト段階にあり、Vulkan から WebGPU へのさらなる移植がどれほど簡単になるかも不明です。完全に標準化された後。したがって、Surreal Engine 用に独自の基本的な OpenGL-ES / WebGL レンダリングを作成する必要がありました。

プロジェクトのビルド

Surreal Engine でシステムを構築 – CMake も移植を簡素化します。 Emscripten はネイティブ ビルダーを提供します。えむけ、えむけ

Surreal Engine の移植は、Death-Mask と呼ばれる、WebGL/OpenGL ES および C++ で作成した最新ゲームのコードに基づいていました。そのため、開発ははるかに簡単で、必要なビルド フラグとコード サンプルがすべて揃っていました。

CMakeLists.txt の最も重要なポイントの 1 つは Emscripten のビルド フラグです。以下はプロジェクト ファイルの例です。


-s MAX_WEBGL_VERSION=2 \

-s EXCEPTION_DEBUG \

-fexceptions \

--preload-file UnrealTournament/ \

--preload-file SurrealEngine.pk3 \

--bind \

--use-preload-plugins \

-Wall \

-Wextra \

-Werror=return-type \

-s USE_SDL=2 \

-s ASSERTIONS=1 \

-w \

-g4 \

-s DISABLE_EXCEPTION_CATCHING=0 \

-O3 \

--no-heap-copy \

-s ALLOW_MEMORY_GROWTH=1 \

-s EXIT_RUNTIME=1")

ビルド スクリプト自体:


emmake make -j 16

cp SurrealEngine.data /srv/http/SurrealEngine/SurrealEngine.data

cp SurrealEngine.js /srv/http/SurrealEngine/SurrealEngine.js

cp SurrealEngine.wasm /srv/http/SurrealEngine/SurrealEngine.wasm

cp ../buildScripts/Emscripten/index.html /srv/http/SurrealEngine/index.html

cp ../buildScripts/Emscripten/background.png /srv/http/SurrealEngine/background.png

次に、インデックスを準備します。 .html 、プロジェクト ファイル システム プリローダーが含まれています。 Web にアップロードするために、Unreal Championship Demo バージョン 338 を使用しました。CMake ファイルからわかるように、解凍されたゲーム フォルダーがビルド ディレクトリに追加され、Emscripten のプリロード ファイルとしてリンクされました。

メインコードの変更

その後、ゲームのゲーム ループを変更する必要がありました。無限ループを実行することはできません。これによりブラウザがフリーズします。代わりに emscripten_set_main_loop を使用する必要があります。この機能については 2017 年のメモ「< a href="https://demensdeum.com /blog/ru/2017/03/29/porting-sdl-c-game-to-html5-emscripten/" rel="noopener" target="_blank">SDL C++ ゲームを HTML5 (Emscripten) に移植」
while ループを終了するコードを if に変更し、ゲーム ループを含むゲーム エンジンのメイン クラスをグローバル スコープに表示し、グローバル オブジェクトからゲーム ループ ステップを呼び出すグローバル関数を作成します。 :


#include <emscripten.h>

Engine *EMSCRIPTEN_GLOBAL_GAME_ENGINE = nullptr;

void emscripten_game_loop_step() {

	EMSCRIPTEN_GLOBAL_GAME_ENGINE->Run();

}

#endif

この後、アプリケーションにバックグラウンド スレッドがないことを確認する必要があります。存在する場合は、シングル スレッド実行用に書き直すか、Emscripten の phtread ライブラリを使用する準備をします。
Surreal Engine のバックグラウンド スレッドは音楽の再生に使用されます。データは、現在のトラック、音楽を再生する必要性、またはその不在に関するメイン エンジン スレッドから取得され、バックグラウンド スレッドはミューテックス経由で新しい状態を受け取り、新しい音楽の再生を開始します。 、または一時停止します。バックグラウンド ストリームは、再生中に音楽をバッファリングするためにも使用されます。
pthread を使用して Emscripten 用の Surreal Engine を構築しようとした試みは失敗しました。SDL2 ポートと OpenAL ポートは pthread サポートなしで構築されており、音楽のために再構築したくなかったためです。そこで、BGM ストリームの機能をループを使用したシングルスレッド実行に移行しました。 C++ コードから pthread 呼び出しを削除することで、バッファリングと音楽再生をメイン スレッドに移動し、遅延が発生しないように、バッファを数秒増やしました。

次に、グラフィックとサウンドの具体的な実装について説明します。

Vulkan はサポートされていません!

はい、Vulkan は HTML5 ではサポートされていませんが、すべてのマーケティング パンフレットでは Vulkan の主な利点としてクロスプラットフォームおよび広範なプラットフォームのサポートが紹介されています。このため、簡素化された OpenGL タイプの基本的なグラフィックス レンダラーを独自に作成する必要がありました。 ES はモバイル デバイスで使用され、最新の OpenGL のファッショナブルな機能が含まれていない場合もありますが、WebGL に非常によく移植されており、まさに Emscripten が実装しているものです。基本的なタイル レンダリング、最も単純な GUI 表示用の bsp レンダリング、およびモデルとマップのレンダリングの作成は 2 週間で完了しました。おそらくこれがこのプロジェクトで最も困難な部分でした。 Surreal Engine レンダリングの全機能を実装するには、まだ多くの作業が必要です。そのため、読者からのコードやプル リクエストの形での支援を歓迎します。

OpenAL がサポートされています!

幸運なことに、Surreal Engine はオーディオ出力に OpenAL を使用しています。 OpenAL で簡単な Hello World を記述し、Emscripten を使用して WebAssembly で組み立てたので、すべてがいかに単純であるかが明らかになり、サウンドの移植に着手しました。
数時間のデバッグ後、Emscripten の OpenAL 実装にはいくつかのバグがあることが明らかになりました。たとえば、モノラル チャネル数の読み取りを初期化するときに、メソッドが無限の数を返し、無限サイズのベクトルを初期化しようとした後、C++例外vector::length_errorでクラッシュします。
>
モノラル チャンネルの数を 2048 にハードコーディングすることで、この問題を回避することができました。


		alcGetIntegerv(alDevice, ALC_STEREO_SOURCES, 1, &stereoSources);



#if __EMSCRIPTEN__

		monoSources = 2048; // for some reason Emscripten's OpenAL gives infinite monoSources count, bug?

#endif



ネットワークはありますか?

Surreal Engine は現在オンライン プレイをサポートしていません。ボットでのプレイはサポートされていますが、これらのボット用の AI を作成する人が必要です。理論的には、Websocket を使用して WebAssembly/Emscripten にネットワーク ゲームを実装できます。

結論

結論として、Emscripten ポートがあるライブラリの使用と、WebAssembly 用に C++ でゲームを実装した過去の経験のおかげで、Surreal Engine の移植は非常にスムーズに完了したと言いたいと思います。エムスクリプテンで。以下は、このトピックに関する知識源とリポジトリへのリンクです。
マ、マ、マ、モンスターを倒せ!

また、できれば WebGL/OpenGL ES レンダリング コードでプロジェクトを支援したい場合は、Telegram で私に連絡してください。
https://t.me/demenscave

リンク

https://demensdeum.com/demos/Sur​​realEngine/
https://github.com/demensdeum/SurrealEngine-Emscripten

https://github.com/dpjudas/SurrealEngine

macOS で USB キーボードのバックライトをオンにする

最近、RGB バックライト付きの非常に安価な Getorix GK-45X USB キーボードを購入しました。 M1 プロセッサを搭載した MacBook Pro に接続してみたところ、RGB バックライトが機能しないことが明らかになりました。 Fn + Scroll Lock の魔法の組み合わせを押してもバックライトは点灯せず、MacBook 画面のバックライト レベルのみが変化しました。
この問題にはいくつかの解決策があります。OpenRGB (機能しない)、HID LED テスト (機能しない) です。 kvmswitch ユーティリティのみが機能しました:
https://github.com/stoutput/OSX-KVM

GitHub からダウンロードし、システム設定のセキュリティ パネルでターミナルからの実行を許可する必要があります。
説明からわかるように、ユーティリティを起動すると、Fn + Scroll Lock キーが送信され、キーボードのバックライトがオン/オフになります。

ツリーソート

ツリーソート – 二分探索ツリーを使用したソート。時間計算量 – O(n²)。このようなツリーでは、左側の各ノードにはノードより小さい番号があり、右側にはノードより大きい番号があります。ルートから来て左から右に値を出力すると、ソートされた番号のリストが得られます。 。驚くべきことですね?

二分探索ツリー図を考えてみましょう。

デリック・クッツェー (パブリック ドメイン)

左下の各ノード、つまり右側のノードごとに、左下隅の最後から 2 番目の左ノードから数字を手動で読み取ってみてください。

次のようになります:

<オル>

  • 左下の最後から2番目のノード – 3.
  • 左分岐があります – 1.
  • この番号 (1) を選択してください
  • 次に頂点 3 (1, 3) を取得します
  • 右側はブランチ 6 ですが、ブランチが含まれています。したがって、同じように読みます。
  • ノード 6 の左分岐 4 (1、3、4)
  • ノード自体は 6 (1、3、4、6) です
  • 右 7 (1、3、4、6、7)
  • ルート ノードに移動します – 8 (1、3、4、6、7、8)
  • 類推により、右側にすべてを出力します
  • 最終的なリストを取得します – 1、3、4、6、7、8、10、13、14
  • コードでアルゴリズムを実装するには、次の 2 つの関数が必要です。

    <オル>

  • 二分探索ツリーの組み立て
  • 二分探索ツリーを正しい順序で出力する
  • 二分探索木は読み取られたときと同じ方法で組み立てられ、それが大きいか小さいかに応じて、左側または右側の各ノードに番号が付けられます。

    Lua での例:

    
    function Node:new(value, lhs, rhs)
        output = {}
        setmetatable(output, self)
        self.__index = self  
        output.value = value
        output.lhs = lhs
        output.rhs = rhs
        output.counter = 1
        return output  
    end
    
    function Node:Increment()
        self.counter = self.counter + 1
    end
    
    function Node:Insert(value)
        if self.lhs ~= nil and self.lhs.value > value then
            self.lhs:Insert(value)
            return
        end
    
        if self.rhs ~= nil and self.rhs.value < value then
            self.rhs:Insert(value)
            return
        end
    
        if self.value == value then
            self:Increment()
            return
        elseif self.value > value then
            if self.lhs == nil then
                self.lhs = Node:new(value, nil, nil)
            else
                self.lhs:Insert(value)
            end
            return
        else
            if self.rhs == nil then
                self.rhs = Node:new(value, nil, nil)
            else
                self.rhs:Insert(value)
            end
            return
        end
    end
    
    function Node:InOrder(output)
        if self.lhs ~= nil then
           output = self.lhs:InOrder(output)
        end
        output = self:printSelf(output)
        if self.rhs ~= nil then
            output = self.rhs:InOrder(output)
        end
        return output
    end
    
    function Node:printSelf(output)
        for i=0,self.counter-1 do
            output = output .. tostring(self.value) .. " "
        end
        return output
    end
    
    function PrintArray(numbers)
        output = ""
        for i=0,#numbers do
            output = output .. tostring(numbers[i]) .. " "
        end    
        print(output)
    end
    
    function Treesort(numbers)
        rootNode = Node:new(numbers[0], nil, nil)
        for i=1,#numbers do
            rootNode:Insert(numbers[i])
        end
        print(rootNode:InOrder(""))
    end
    
    
    numbersCount = 10
    maxNumber = 9
    
    numbers = {}
    
    for i=0,numbersCount-1 do
        numbers[i] = math.random(0, maxNumber)
    end
    
    PrintArray(numbers)
    Treesort(numbers)

    Важный нюанс что для чисел которые равны вершине придумано множество интересных механизмов подцепления к ноде, я же просто добавил счетчик к классу вершины, при распечатке числа возвращаются по счетчику.

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/treesort

    Источники

    TreeSort Algorithm Explained and Implemented with Examples in Java | Sorting Algorithms | Geekific – YouTube

    Tree sort – YouTube

    Convert Sorted Array to Binary Search Tree (LeetCode 108. Algorithm Explained) – YouTube

    Sorting algorithms/Tree sort on a linked list – Rosetta Code

    Tree Sort – GeeksforGeeks

    Tree sort – Wikipedia

    How to handle duplicates in Binary Search Tree? – GeeksforGeeks

    Tree Sort | GeeksforGeeks – YouTube

    バケットソート

    バケット並べ替え – バケットごとに並べ替えます。このアルゴリズムはカウンティングソートに似ていますが、数値が「バケット」範囲に収集され、その後、他の十分に生産性の高いソートアルゴリズムを使用してバケットがソートされ、最後のステップで「バケット」の範囲が展開される点が異なります。 1 ずつ増やすと、ソートされたリストが得られます。

    アルゴリズムの時間計算量は O(nk) です。このアルゴリズムは、一様分布の法則に従うデータに対して線形時間で機能します。簡単に言うと、要素は「スパイク」のない特定の範囲内にある必要があります (たとえば、0.0 から 1.0 までの数値)。そのような数字の中に 4 または 999 がある場合、中庭法によれば、そのような列は「偶数」とみなされなくなります。

    Julia での実装例:

        buckets = Vector{Vector{Int}}()
        
        for i in 0:bucketsCount - 1
            bucket = Vector{Int}()
            push!(buckets, bucket)
        end
    
        maxNumber = maximum(numbers)
    
        for i in 0:length(numbers) - 1
            bucketIndex = 1 + Int(floor(bucketsCount * numbers[1 + i] / (maxNumber + 1)))
            push!(buckets[bucketIndex], numbers[1 + i])
        end
    
        for i in 0:length(buckets) - 1
            bucketIndex = 1 + i
            buckets[bucketIndex] = sort(buckets[bucketIndex])
        end
    
        flat = [(buckets...)...]
        print(flat, "\n")
    
    end
    
    numbersCount = 10
    maxNumber = 10
    numbers = rand(1:maxNumber, numbersCount)
    print(numbers,"\n")
    bucketsCount = 10
    bucketSort(numbers, bucketsCount)

    На производительность алгоритма также влияет число ведер, для большего количества чисел лучше взять большее число ведер (Algorithms in a nutshell by George T. Heineman)

    Ссылки

    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/bucketSort

    Источники

    https://www.youtube.com/watch?v=VuXbEb5ywrU
    https://www.youtube.com/watch?v=ELrhrrCjDOA
    https://medium.com/karuna-sehgal/an-introduction-to-bucket-sort-62aa5325d124
    https://www.geeksforgeeks.org/bucket-sort-2/
    https://ru.wikipedia.org/wiki/%D0%91%D0%BB%D0%BE%D1%87%D0%BD%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0
    https://www.youtube.com/watch?v=LPrF9yEKTks
    https://en.wikipedia.org/wiki/Bucket_sort
    https://julialang.org/
    https://www.oreilly.com/library/view/algorithms-in-a/9780596516246/ch04s08.html