この記事では、きれいな戦車モデルをシーンにインポートし、
websocket
と
asyncio
を使用してプレイヤーとボットを互いに同期させ、部屋間でバランスをとることから、ブラウザーベースの
3D
シューティングゲームの作成について説明しようとしました。
はじめに
1.ゲームの構造
2.モデルとブレンダーをインポートする
3. babylon.jsとモデル自体を使用してゲームにモデルをロードする
4. babylon.jsの動き、ミニマップ、およびゲームサウンド
5. Webソケットとゲームの同期
6.プレイヤーとその調整
7.部屋とオブジェクトのPythonによるプレイヤーのバランス調整
8. Asyncioおよびボットの動作生成
9. Nginxおよびソケットプロキシ
10. memcacheを介した非同期キャッシング
11.あとがきとロードマップ
Python3
、
WebGL
、および単なるゲームの非同期アプリケーションのトピックに興味のある方は、猫をお願いします。
はじめに
この記事自体は、 aiohttpと
asyncio
を使用して非同期アプリケーションを作成するトピックの続きとして考えられていました。 最初の部分が
aiohttp( asyncio )
上に
django
ようなモジュール構造を作成する方法に専念していた場合、 2番目の部分ではもっと創造的なことをやりたいと思いました。
当然のことながら、
3D
おもちゃを作ることは私にとっては面白そうで、ゲーム以外では非同期プログラミングが必要な場合があります。
登録せずに、お互い、ボット、部屋、簡単なチャット、簡単な風景をたたくことができるおもちゃを用意します。 ゲームモデルとして、最もシンプルで馴染みのあるいくつかの戦車と、同時に指標となるゲームモデルを取り上げます。 さて、フレームワークの一部として、
memcached
キャッシュを思い出しましょう。
わかりやすくするために、一部のプロセスは、リモートでインフォグラフィックを連想させる写真で表されます。これは、ネットワークプログラミングが、送信されているものと呼び出されているものを矢印で示している場合、常に簡単で便利に見えるとは限らないためです。
インフォグラフィックと通常の形式の両方のコード例は、一般的な作業スキームの理解を深めるために、非常に頻繁に簡略化されます。 さらに、最新の修正を含む完全なコードはgithubで表示できます。
しかし、これは完全なゲームではないことを理解する必要があります-彼が
git clone
を書いてATMに行ったという意味で。 むしろ、ゲームフレームワーク、1つのボトルでの
asyncio
および
webgl
デモを作成する試みです。 ショーケース、評価、徹底的にテストされたセキュリティなどはありませんが、一方で、
open sourse
開発された
open sourse
プロジェクトでは、暇なときに十分に機能したようです。
2モデルとブレンダーのインポート
当然、おもちゃにはキャラクターの
3D
モデルが必要であり、風景や建物などをシミュレートするモデルが必要です。
キャラクターは、人、戦車、飛行機です。 2つのオプションがあります-モデルを描画し、完成したものをインポートします。 最も簡単な方法は、専門サイトの1つで既製のモデルを見つけることです(たとえば、 hereまたはhere) 。 戦車や他のモデルを描くプロセスに興味がある人、YouTubeにはたくさんのビデオがあり、ハブにはこのトピックに関する資料があります 。
インポートプロセス自体について詳しく説明します。
blender
にインポートされた形式の中で、
.die .obj .3ds
が最もよく見られます。
インポート/エクスポートには多くのニュアンスがあります。 たとえば、
.3ds
をインポートする場合、原則として、モデルはテクスチャなしでインポートされますが、マテリアルは既に作成されています。 この場合、ディスクテクスチャから各マテリアルをロードするだけです。
.obj
場合、原則として、テクスチャに加えて、
.mtl
ファイルが存在する場合、それが存在する場合、通常は問題の可能性は低くなります。
モデルをシーンにエクスポートした後、
webgl
表示に問題があるという警告とともに、
chrome
クラッシュすることがあります。 この場合、たとえば、衝突、アニメーションなどがある場合、ブレンダーで余分なものをすべて削除するようにしてください。
さらに、最も重要なポイントの1つです。 マップ内を移動するモデルについては、
モデルを構成するすべてのオブジェクトを接着する必要があります。 そうしないと、それらを移動することはできません。視覚的には、タンクの代わりにマシンガンのみが駆動するように見えます。また、ツリーに必要な座標を設定しようとすると、マップ上の位置は木の切り株のみを変更し、他のすべてはマップの中央に残ります。
この問題を解決するには2つの方法があります。
1)モデルのすべての詳細を組み合わせて、1つのオブジェクトにします。 この方法は少し高速ですが、
UV
スキャンの形式で1つのテクスチャがある場合にのみ機能します。 これを行うには、シフト付きのアウトライナを使用してすべてのオブジェクトを選択します。それらは特徴的なオレンジ色で強調表示され、
object
メニューで
join
アイテムを選択します。
2)次のオプションは、親子の原則に従ってすべての詳細をリンクすることです。 この場合、各ディテールに独自のテクスチャがあったとしても、テクスチャに問題はありません。 これを行うには、右クリックして親オブジェクトと子を順に選択し、
ctrl+P
を押してメニューから
object
を選択します。 その結果、オフラインモードでは、モデルを構成するすべてのオブジェクトが同じ親に属していることがわかります。
それでも、私はそのような発言を挿入したいと思います。多くの場合、いくつかのモデルは、不明な理由により、ブレンダーにインポートされないか、何らかのジャンクの形でインポートされます。 また、非常に多くの場合、モデルに付属している一部のテクスチャは適用したくない場合があります。 そのような場合、何もできません。他のオプションに進む必要があります。
3. babylon.jsとモデル自体を使用してゲームにモデルをロードする
モデル自体のロードは非常に単純に見え、ディスク上のメッシュの場所を示します。
loader = new BABYLON.AssetsManager(scene); mesh = loader.addMeshTask('enemy1', "", "/static/game/t3/", "t.babylon");
その後、モデルがすでにロードされていることを任意の場所で確認し、必要に応じてその位置を設定して変換することができます。
mesh.onSuccess = function (task) { task.loadedMeshes[0].position = new BABYLON.Vector3(10, 2, 20); };
この場合の最も一般的な操作の1つは、オブジェクトのクローン作成です。たとえば、ツリーがあります。各ツリーを個別にロードしないように、一度ロードして、異なる座標でシーン全体にクローンを作成できます。
var palm = loader.addMeshTask('palm', "", "/static/game/g6/", "untitled.babylon"); palm.onSuccess = function (task) { var p = task.loadedMeshes[0]; p.position = new BABYLON.Vector3(25, -2, 25); var p1 = p.clone('p1'); p1.position = new BABYLON.Vector3(10, -2, 20); var p2 = p.clone('p2'); p2.position = new BABYLON.Vector3(15, -2, 30); };
AssetsManager
の場合、クローニングも重要な役割を果たします。 彼はシーンの主要部分がロードされるまで単純なスプラッシュ画面を描画し、
loader.onFinish
に入れた
loader.onFinish
ことを確認します。
var createScene = function () { . . . } var scene = createScene(); loader.onFinish = function (tasks) { engine.runRenderLoop(function () { scene.render(); }); };
さまざまな理由により、ゲーム中にそれ以上ロードすることは避けなければなりません。 したがって、すべてのキャラクターは初期化中にロードされ、すでにソケットの処理や、プレーヤーの外観と動作を担当するクラスで、必要な機器などのクローンを作成します。 スキームは次のようになります。
さらに、モデル自体について少し説明したいと思います。このバージョンのカードは既成のソリューションというよりも実験的なものですが、全体像を理解するのに害はありません。
この場合、キャラクターは2種類の戦車、T-90とエイブラムスによって表されます。 勝利と敗北のゲームロジックはないため、フレームワークの場合は、個々のケースごとにこれらすべてを発明する必要があることが暗示されています。 だから今は選択肢がなく、最初の人は常にエイブラムスをプレイし、ボットと他のすべてのプレイヤーはT-90として表示されます。
マップ自体には、特定のレリーフがあります。heightMapと呼ばれる
babylon.js
を使用して非常に簡単に作成されます。このため、土壌テクスチャに黒と白の画像を適用する必要があります。丘では、特性の一部をパラメータで指定できます。暗い部分と白い部分の間の遷移がぼやけているほど、勾配は緩やかになります。
var ground = BABYLON.Mesh.CreateGroundFromHeightMap("ground", "/static/HMap.png", 200, 200, 70, 0, 10, scene, false); var groundMaterial = new BABYLON.StandardMaterial("ground", scene); groundMaterial.diffuseTexture = new BABYLON.Texture("/static/ground.jpg", scene); ground.material = groundMaterial;
さらに、家の形の小さな側近、その近くの給水塔、いくつかの木といくつかの草があります。
芝生は、最大の低ポリで、テクスチャがその上にあるプレーンで出てきました。 そして、この飛行機はさまざまな場所で傾いています。 一般的に、ポリゴンモデルが低ければ低いほど、パフォーマンスは向上しますが、明らかな理由により、エンターテイメントが損なわれます。
もちろん、設定「グラフィック品質」で選択を行うことはできますが、私たちの場合はできません。
草とは異なり、バナナのヤシにはかなりの数のピークがあるため、マップ上に数個のピースのみを残すことにしました。
マップ上の頂点が多いほど、
FPS
は低くなります。
家は少し離れており、衝突のある透明な立方体で覆うことが決定されました。
そして最後に残したものは3つのカラフルなキューブです。これらはすべてのユーザーに対して同期されておらず、単純なターゲットを表しています。 それぞれにヒットすると、点灯して消えます。
4. babylon.jsの動き、ミニマップ、およびゲームサウンド
動きについて言えば、これは主に最初の人に関するものです。他のプレイヤーやボットの動きは常に位置の変化であり、ほとんどの場合、サーバーからのソケットを使用してブロードキャストされます。
それ自体では、動きは単にカメラとプレーヤーの目に見える部分を制御するだけです。 たとえば、プレーヤーが右に曲がるので、カメラを私たちが見ている場所に対して右に回すか、シーンを目的の程度に回転させる必要があります。 また、たとえば、敵を倒すためのいくつかの手段を描いたモデルも有効にする必要があります。
基本的に一人称の動きのために
babylon.js
で作られたゲームでは、2つのカメラがあります:
-
FreeCamera
原則として、キャラクターの親であり、キャラクターは単純にそれに従います。高度な技術、人や飛行するすべてのものに使用するのに非常に便利ですFreeCamera
は慣性と速度を調整する機能があり、これも非常に重要です。 -
FollowCamera
反対に、これはある種のオブジェクトをFollowCamera
するカメラです。マウスとキーボードからのコントロールが異なる場合に使用すると便利です。 つまり、レビューは移動の方向に依存しません。
例:
//FollowCamera var camera = new BABYLON.FollowCamera("camera1", new BABYLON.Vector3(0, 2, 0), scene); camera.target = mesh; ``````javascript //FreeCamera var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3( 0, 2, 0), scene); mesh.parent = camera;
移動中、ヒット中、ショット中、移動中などにいくつかの音があるはずです。
babylon.js
には優れたサウンド管理APIがあります 。 サウンドコントロールのトピックは非常に広範囲であるため、いくつかの小さな例を検討します。
初期化の例:
var music = new BABYLON.Sound("Music", "music.wav", scene, null, { playbackRate:0.5, // . volume: 0.1, // . loop: true, // . autoplay: true // . });
ショットの音の例-クリックすると、インターロックが解除されていることを確認し、シングルショットの音を再生します。
var gunS = new BABYLON.Sound("gunshot", "static/gun.wav", scene); window.addEventListener("mousedown", function (e) { if (!lock && e.button === 0) gunS.play(); });
矢印を前後に押して、それに応じて再生を開始した場合に、機器の動きの音を停止します。 キーリリースイベントで、再生を停止します。
var ms = new BABYLON.Sound("mss", "static/move.mp3", scene, null, { loop: true, autoplay: false }); document.addEventListener("keydown", function(e){ switch (e.keyCode) { case 38: case 40: case 83: case 87: if (!ms.isPlaying) ms.play(); break; } }); document.addEventListener("keyup", function(e){ switch (e.keyCode) { case 38: case 40: case 83: case 87: if (ms.isPlaying) ms.pause(); break; } });
ミニマップ
シューティングゲームには、プレイヤーだけを見ることができるミニマップ、または一度にすべてを見ることができるミニマップが必要です。この場合、よく見ると、シェルを見ることができます。
babylon.js
これを実装する方法はいくつかあります。 おそらく最も簡単な方法は、別のカメラを作成し、マップの上に配置し、このカメラからのビューを必要なコーナーに配置することです。
私たちの場合、
freeCamera
を使用して、
freeCamera
を一番上に配置するように伝えます。
camera2 = new BABYLON.FreeCamera("minimap", new BABYLON.Vector3(0,170,0), scene);
Y
座標が大きいほど、ビューは完全になりますが、マップの詳細は細かくなります。
次に、カメラに画像をスクリーンに配置する方法をカメラに伝えます。
camera2.viewport = new BABYLON.Viewport(x, y, width, height);
最後に、両方のカメラをシーンに追加する必要があります(カメラが1台の場合、これを行う必要はありません)。
scene.activeCameras.push(camera); scene.activeCameras.push(camera2);
5. Webソケットとゲームの同期
ゲームの基本構造全体が
websoket
構築され、プレイヤーは何らかのアクション、マウスの回転、またはキーストロークを実行し、プレイヤーの位置座標がサーバーに送信されるイベントがこの動きでハングアップします。部屋。
最初は、
FreeCamera
を使用するため、親オブジェクトであるため、その座標を使用します。 例:
-
camera.cameraRotation
軸に沿った回転のX
およびY
座標が含まれます。 -
camera.position
マップ上のメッシュの位置のX
、Y
、Z
座標が含まれます。
次の図では、サーバーへの接続を開いて新しいプレーヤーを作成してから、ユーザーのアクションに応答するときにメッセージを交換するまでのプロセス例を示しています。
6.サーバー側のデバイス
上記では、非常に簡潔な簡単な図を見ましたが、今度はサーバー側の動作について詳しく説明します。 ソケットハンドラーの関数では、次のメッセージを受信した後、その
action
を確認し、次にプレーヤーが実行した
action
を確認し、これに従って目的の関数、イベントハンドラーを呼び出します。
async def game_handler(request): . . . async for msg in ws: if msg.tp == MsgType.text: if msg.data == 'close': await ws.close() else: e = json.loads( msg.data ) action = e['e'] if action in handlers: handler = handlers[action] handler(ws, e) . . .
たとえば、
move
が来た場合、それはプレイヤーがいくつかの座標に移動したことを意味します。 このアクションの関数ハンドラーでは、これらの座標を
Player
クラスに割り当てるだけで、それらの座標が処理されて戻され、さらに現在の部屋の他のすべてのプレーヤーに送信されます。
def h_move(me, e): me.player.set_pos(e['x'], e['y'], e['z']) mess = dict(e="move", id=me.player.id, **me.player.pos_as_dict) me.player.room.send_all(mess, except_=(me.player,))
もちろん、現在
Player
クラスが座標で行うことはすべて、単純にそれらをボットに渡して、焦点を合わせます。 そして理想的には、座標でマップ全体をチェックする必要があります。プレーヤーが障害に遭遇した場合、たとえば不正行為を避けるために、クライアントでスクリプトが変更された場合、壁から漏れないようにする必要があります。
class Player(list): . . . def __init__(self, client, room, x=0, y=0, z=0, a=0, b=0): list.__init__(self, (x, y, z, a, b)) self._client = client self._room = room Player.last_id += 1 self._id = Player.last_id room.add_player(self) . . . def set_rot(self, a, b): self[3:5] = a, b def getX(self): return self[0] . . . def setX(self, newX): self[0] = newX . . . x = property(getX, setX) . . . @property def pos_as_dict(self): return dict(zip(('x', 'y', 'z'), self.pos))
Player
クラスでは、
property
を使用して、座標をより便利に処理します。 興味深いのは、ハブラでこのテーマに関する良い資料があったことです。
7.部屋ごとのプレイヤーのバランス
ゲームのかなり重要な部分は、異なる氏族、部屋、惑星、国などでプレイヤーを育てることです。 それ以外の場合、すべてが1枚のカードに収まらないからです。 私たちの場合、システムはまだ非常にシンプルです-1つの部屋に設定で設定されているよりも多くのプレイヤーがいる場合(デフォルトではボットで4)、新しい部屋を作成し、残りのプレイヤーをそこに円で送信します。
現時点では、ショーケースとレーティングがないため、何らかのベースを使用する意味がないため、どの部屋のどのプレーヤーなどに関するすべての情報がメモリに保存されます。
ルーム番号は、プレイヤーがルート
/pregame
あるスタートページからゲームに入るときに割り当てられます。 ボタンが押されると、
ajax
トリガーされます。結果が成功した場合は、プレーヤーが目的の部屋にリダイレクトされます。
if (data.result == 'ok') { window.location = '/game#'+data.room;
サーバー側では、すべての部屋とその中のプレイヤーのリストを含む
rooms
ディクショナリ
id
ます。プレイヤーの数が指定された値を超えていない場合は、部屋
id
クライアントに返します。 プレイヤーの数が多い場合は、新しい部屋を作成します。
def check_room(request): found = None for _id, room in rooms.items(): if len(room.players) < 3: found = _id break else: while not found: _id = uuid4().hex[:3] if _id not in rooms: found = _id
Room
については、
Room
クラスを担当します。 彼の一般的な作業スキームは次のようになります。
Player
クラスと相互作用することがわかります。おそらく、スキーム全体は完全に線形に見えませんが、最終的には、このようなチェーンを非常に便利に書くことができます。
# me.player.room.send_all( {"e" : "move", . . . }) # me.player.room.players # me.player.room.add_player(self) # me.player.room.remove_player( me.player )
私は、私の同僚について、この提起された質問について少し話をしたいと思います。
me
は、イベントを提供する関数のパラメーターとして渡されるソケットです。
def h_new(me, e): me.player = Player(me, Room.get( room_id ), x, z)
ここで、実際には、翻訳者が言うように、しゃれ。 Pythonのすべてがオブジェクトであることを知っているので。
これはより明確に起こることです:
player = Player( Room.get(room_id), x, z) player.me = me me.player = player
player
モジュールと
me
オブジェクトの
.player
という2つのリンクを取得します。両方とも等しく、少なくとも1つのリンクが存在する限り存在するメモリ内の同じオブジェクトを参照します。
これはさらに簡単な例で見ることができます:
>>> a = {1} >>> b = a >>> b.add(2) >>> b {1, 2} >>> a {1, 2}
この例では、
b
と
a
は1つの共通値への単なる参照です。
>>> a.add(3) >>> a {1, 2, 3} >>> b {1, 2, 3}
さらに調査します。
>>> class A(object): ... pass ... >>> a = A() >>> a.player = b >>> a.player {1, 2, 3} >>> b {1, 2, 3} >>> a.__dict__ {'player': {1, 2, 3}}
オブジェクトのプロパティは単なる構文糖です。 この場合、それらは単に
__dict__
辞書に格納されます
その結果、リンク
a
1つを強制終了しまし
a
、その代わりに、新しく作成したオブジェクトに属する別のリンクを作成し、実際にこのオブジェクトの辞書
__dict__
ます。
>>> a <__main__.A object at 0x7f3040db91d0>
8. Asyncioおよびボットの動作生成
通常のゲームでは、少なくとも1つのボットが必要です。このゲームも例外ではありません。 もちろん、ボットができるのは同心円を描くことだけで、プレイヤーがいる座標に徐々に近づいていきます。 新しいプレーヤーが入ると、ボットはそれに注意を向けます。
ボットが移動する座標に関する部屋内のすべてのプレイヤーにメッセージを送信する行。
mess = dict(e="move", bot=1, id=self.id, **self.pos_as_dict) self.room.send_all(mess, except_=(self,))
次のようにクライアント部分とボットとの相互作用の一般的なスキームは次のとおりです。
クラスでは
Room
、私たちは、
__init__
クラスのインスタンスを作成します
Bot
。そして、すでに
def __init__
クラスで
Bot
、私たちは
asyncio.async(self.update())
各パス上で実行されなければならないタスクをperedaom。
を含む関数を呼び出しても
await
、関数自体は開始されませんが、ジェネレーターオブジェクトが作成されます。
class
この関数は開始しないため、宣言された関数を呼び出すだけでなく、このクラスが提供するオブジェクトを作成します。
await
ジェネレーターでメソッドが呼び出されると、含む関数の呼び出しが発生します
.__next__()
。この場合-
next
デコレータにあり
async
-コルーチンを初期化します。
簡単に言えば、100ミリ秒ごとにボットの新しい座標を使用してクライアントにメッセージを送信し、0.5秒ごとにボットの座標を更新します。
無限ループでタスクを操作する簡単な例:
import asyncio async def test( name ): ctr = 0 while True: await asyncio.sleep(2) ctr += 1 print("Task {}: test({})".format( ctr, name )) asyncio.ensure_future( test("A") ) asyncio.ensure_future( test("B") ) asyncio.ensure_future( test("C") ) loop = asyncio.get_event_loop() loop.run_forever( )
入れたすべての関数は、2秒で
asyncio.ensure_future
指定さ
asyncio.sleep(2)
れた遅延で円で実行されます)。これの実用的な用途は非常に広範囲であり、ゲーム用のボットに加えて、たとえばトレーディングシステム用のボットだけを書くことができます。私の主観的な意見では、これは場所での開発を簡素化し、非常に価値があり、動物園を回避します。
9. Nginxおよびソケットプロキシ
そしてゲームに関して言及する最後のことは、
Nginx
私たちのプロジェクトが確実に動作する
websocket
と確信している場合の正しい設定です
http
。最初に思い浮かぶのは、この構成のようなものです:
server { server_name aio.dev; location / { proxy_pass http://127.0.0.1:8080; } }
また、ローカルで正常に動作しますが、1つの根本的な欠点があります。ソケットは、この設定では外部サーバーで動作し
5.5.5.10
なくなり
loalhost
ます。
したがって、次のアイデアは書くことです。
server { server_name aio.dev; location / { proxy_pass http://5.5.0.10:8080; } }
しかし、彼女はPythonのパフォーマンス性能の低いので、あまりにも、欠陥がある
nginx
桁違いによると、いずれの場合に
proxy_pass
しなければならない
http://127.0.0.1:8080
ので、機会を取る
Nginx
ソケットをプロキシ、数年前に登場し-aを、:
server { server_name aio.dev; location / { proxy_pass http://127.0.0.1:8080; } location /ws { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }
ソケットを初期化するときのアドレスには、ポート80を指定します。これは、明示的に指定しない限り、デフォルトでポート80でリッスンするように構成されている
var uri = "ws://aio.dev:80/ws"
ため
Nginx
です
listen
。
この構成では、それがために働くだろう
Nginx
番目、そして便利にアクセス可能となり
websoket
、S、および
http
。
10.非同期キャッシュ。
このゲームはフレームワークのプラグインコンポーネントの1つとして記述されているため、小さなイノベーションについて少し説明したいと思います。
その
memcached
ため、で関数を使用するページキャッシングの例を考えてみましょう
async def
。多かれ少なかれ訪問したクラシックサイトでは、メインページ、ニュースページなどの訪問したページをキャッシュできるほか、ページが変更されたときにキャッシュ時間が切れる前にキャッシュをリセットできるようにする必要があります。幸いなことに、memcached の非同期ドライバーは既にsvetlovによって作成されており、デコレーターを作成し、いくつかの小さな問題を解決するために残っています。
それ自体では、たとえば、次のように、任意の関数に対してデコレータの形でかなり馴染みのある方法でキャッシングを行うことが決定されました。ビーカー。デコレータでは、キャッシュ時間を設定し、特にmemcachedのキーとなる名前を設定する必要があります。
データ
memcached
はシリアル化されて使用され
pickle
ます。そしてもう1つのニュアンス-フレームワークは最上部
aiohttp
に記述さ
CIMultiDict
れているため、シリアル化されていません。同じキーを持つ機能を備えた辞書の実装であり、作成
Cython
者により高速に記述されています
aiohttp
。
dct = CIMultiDict() print( dct ) <CIMultiDict {}> dct = MultiDict({'1':['www', 333]}) print( dct ) <MultiDict {'1': ['www', 333]}> dct = MultiDict([('a', 'b'), ('a', 'c')]) print( dct ) <MultiDict {'a': 'b', 'a': 'c'}> dct = dict([('a', 'b'), ('a', 'c')]) print( dct ) {'a': 'c'}
したがって、そこに格納された値はでシリアル化できませんでした
pickle
。したがって、それらを取得して再梱包する必要がありましたが、時間が経つにつれて
CIMultiDict
シリアル化可能になることを願ってい
pickle
ます。
d = MultiDict([('a', 'b'), ('a', 'c')]) prepared = [(k, v) for k, v in d.items()] saved = pickle.dumps(prepared) restored = pickle.loads(saved) refined = MultiDict( restored )
完全なキャッシングコード
def cache(name, expire=0): def decorator(func): async def wrapper(request=None, **kwargs): args = [r for r in [request] if isinstance(r, aiohttp.web_reqrep.Request)] key = cache_key(name, kwargs) mc = request.app.mc value = await mc.get(key) if value is None: value = await func(*args, **kwargs) v_h = {} if isinstance(value, web.Response): v_h = value._headers value._headers = [(k, v) for k, v in value._headers.items()] await mc.set(key, pickle.dumps(value, protocol=pickle.HIGHEST_PROTOCOL), exptime=expire) if isinstance(value, web.Response): value._headers = v_h else: value = pickle.loads(value) if isinstance(value, web.Response): value._headers = CIMultiDict(value._headers) return value return wrapper return decorator
キャッシングは、デコレータを一番上に記述し、キャッシュの有効期限と名前を指定するだけで適用できます。
from core.union import cache @cache('list_cached', expire=10 ) async def list_tags(request): return templ('list_tags', request, {})
あとがき
この段階では、それはむしろ、可能性を実証するために、可能性の高いゲームの初期の実施段階であると
WebGL
し、
asyncio
製品版より。しかし、フレームワークの将来のバージョンでは、すべてが理想にはるかに近づくことを願っています。空中、地上、そして単に個々のプレイヤーとして、プレイヤーが戦闘のキャラクターを選択したり動的に変更したりできるスペースサガの形でゲームを作りたいと思います。
私は常にスターウォーズのキャラクターが好きだったので、歴史的なものよりも未来的な方法ですべてをやりたいです。
設定でゲームの任意のレベルをローカライズし、それをメインのレベルにし、可能な限りゲームのカードとキャラクターを動的に変更する機能を備えています。
以下は、主に、ある程度言える程度に現在存在し、最も可能性の高い、さらなる作業が行われる主な欠点です。
ping
サーバーは各プレーヤーからのpingを監視し、ヒット、移動などの速度を同期しようとする必要があります。少なくともおよそ。新しいプレイヤーが参加するとき、彼は対戦相手がほぼ同じpingを持っている部屋に入る必要があります。商業用ですが、さまざまなオプションが思い浮かびます。
不正行為
当然、クライアントを決して信頼するべきではありません。通常のサーバーでは、マップ全体を完全に理解し、プレーヤーのすべての動き、弾丸の軌道をチェックする必要があります。そうでなければ、ゲームの最小の人気で、多様な不正行為の繁栄は避けられないでしょう。
ロードマップ-ゲーム
クライアント:
- ボット射撃、より多くの武器。
- タンク制御-タワーのみがマウスで回転し、タンク自体は矢印で回転します
- さまざまな角度からの動き
- 宇宙/空戦用のマップとコントロールの追加
- アーバンテラーのようなフィールドに歩兵戦争マップとキャラクターを追加する
サーバー:
- すべての動きを確認する
- ショットの方向を確認する
- ボットの機能を拡張する(たとえば、ボットの数を増やす、動的に減らすなど)
- ボットのベースAI
ロードマップ-フレームワーク全体
- 小さいCMS
- 倉庫会計のコンストラクター(ミニERP)
- レポートデザイナー
- MongoDBのWebクライアント
- デモミニソーシャルネットワーク
何かを書くのを忘れて、どこかで彼は用語と間違えられるかもしれません。したがって、すべての文法的およびその他のエラーをPMに書き込むようお願いします。
前編
babylon.jsに関する記事を確認し、 githubのthree.js
ライブラリと比較する
ドキュメントを読む
無料の大規模な選択を主要なサイトの一つとは、3Dモデルを支払った
ブレンダーのための無料の3Dモデルとウェブサイトを
回転
babylon.jsの中の音と作業
音の可視化例
asynioにネロの世界
スリープ
タスクと作業
ステッチ通話
PEP-0492
のブログ Svetlovの aiohttp著者
memcachedの非同期ドライバ
babylon.jsのための更新されたドキュメント
githubの上の文書aiohttp
aiohttpドキュメントreadthedocsのための
歩留まり文からドキュメント
ライブラリのリスト- AIO-LIBS
の別のより包括的なリスト
:発電機の詳細
http://www.dabeaz.com/generators/Generators。 pdf
http://www.dabeaz.com/coroutines/Coroutines.pdf
http://www.dabeaz.com/finalgenerator/FinalGenerator.pdf