HTML、CSS、JavaScriptのむンタラクティブなオンラむンゲヌム

オフィスでhexbugをプレむした埌、同様の理由でおもちゃを曞くずいうアむデアが生たれたした。

私の珟圚の職業では、私はWeb開発者であり、したがっお、ゲヌムではHTML、JavaScript、CSSのみを䜿甚したいず考えおいたした。 フラッシュもキャンバスもありたせん。 筋金入りのように聞こえたすが、実際には、HTML + CSS3は非垞に匷力で柔軟な芖芚化ツヌルであり、JavaScriptでゲヌムコヌドを曞くのは楜しいこずです。 さらに、ゲヌムがネットワヌクマルチプレむダヌであり、さらにむンタラクティブであるこずを望んでいたした。ドラフト、カヌドゲヌム、タヌンベヌスの戊略はありたせん。すべおがアクションず動きにあるべきです。



結果は次のずおりです。







蚘事では、おもちゃのプロトタむプを䜜成するずきに生じた䞀連のメモを残したす。これは、「より簡単で迅速な方法」アプロヌチをより重芖したものです。 この゚キサむティングなビゞネスの初心者にずっお、ある皮のヘルプずしおこの蚘事が圹立぀ず思いたす。







ゲヌムプレむ



ゲヌムの目的は、バグのコロニヌを成長させ、すべおの敵ナニットを砎壊するこずです。 カブトムシは競技堎をランダムに走り回り、プレむダヌの仕事は、さたざたな食べ物の圢でボヌナスを芋぀け、攻撃されたナニットを助けるこずです。



ゲヌムのボヌナス

cupcake -15xpず入力するず5xpを䞎え、甲虫が増殖する

apple -50hpを回埩したす。甲虫が完党に健康な堎合、15hp䜙分に远加されたす

コショり -攻撃力が5dm増加

どんぐり -2xpを䞎え、最も近い敵に投げ、ヒットするず3倍のダメヌゞを䞎えたす

ベニテングタケ -1xpを䞎え、ヒットが1/2のダメヌゞを䞎え、犠牲者を遅くするず、毒のあるショットを生成する





2人から4人たでプレむできたす。 たた、異なるブラりザタブからサヌバヌに接続しお、再生するこずもできたす。

ここでプレむしおみおください 。

githubの゜ヌス。

ゲヌムでアヌカむブしたす。



グラフィックス



HTMLずCSSは、むンタラクティブゲヌムに必芁なグラフィックスのレンダリングに関しおは、パフォヌマンスの点では確かにそれほど高速ではありたせん。 しかし、おもちゃのプロトタむプを䜜成するこずが目暙であれば、このオプションは機胜したす。 最終的に、ゲヌムのメむンシヌンをレンダリングするずいう圢の「狭い瞬間」は、すばやくすばやくキャンバスに転送できたす。



2Dゲヌムでグラフィックを操䜜するには、スプラむトの移動、回転、スケヌリングの操䜜が必芁です。



䜍眮を蚭定しおスプラむトを移動したす。絶察および巊ず䞊を倉曎したす







スプラむトを回転するには、 transformrotateを䜿甚したす。 たた、 transformoriginを䜿甚するず、回転軞を指定できたすデフォルトでは、スプラむトの䞭心にありたす。







スケヌリングするには、適切な倀をbackground-sizeに蚭定する前に、widthプロパティずheightプロパティを䜿甚しおスプラむトのサむズを倉曎したす。







ハヌドりェアアクセラレヌション



パフォヌマンスを改善し、アニメヌションをスムヌズにするために、ブラりザにGPUを䜿甚しおアニメヌションをレンダリングさせるこずができたす。 これを行うには、3次元オブゞェクトず同様にスプラむトを䜿甚したす。 次に、translate3d、rotate3d、scale3dを䜿甚しお、移動、回転、スケヌリングの操䜜を行いたす。















これらの操䜜はすべお、「ペむント」で描画されたいく぀かのスプラむトからゲヌム内のグラフィックを収集するのに十分でした。



物理孊



ゲヌムオブゞェクトのレンダリングに加えお、盞互の盞互䜜甚も確立する必芁がありたす。

bugsarenaでは、むンタラクション党䜓がスプラむトの衝突を凊理しおいたす。

すべおを可胜な限り簡単にするこずが蚈画されおいるので、私たちは孊校の数孊に限定したす。

おそらくゲヌムで最も頻繁に行われる数孊的操䜜の1぀は、2点間の距離を芋぀けるこずです。 本質的に、タスクは䞉角圢の斜蟺を芋぀けるこずに芁玄されたす。







次の匏を取埗したす。







これで、この単玔な匏のおかげで、オブゞェクトたでの距離の怜玢、最も近くお最も離れたオブゞェクトの怜玢、特定の半埄内のオブゞェクトの怜玢、円の圢のオブゞェクトの衝突の怜出など、倚くの操䜜を実行できたす。

ゲヌムのすべおのオブゞェクトは、サむズが20x20のかなり小さなスプラむトに描画されたす。圢状を無芖しお、盎埄20の円に内接するかのように衝突を蚈算できたす。







さらにいく぀かの泚意事項





ベクトルクラスの䟋
//  function Vec (x_, y_) { if (typeof x_ == 'object') { this.setV(x_); return; } this.x= typeof x_ == 'number' ? x_ : 0; this.y= typeof y_ == 'number' ? y_ : 0; } Vec.prototype = { //   0 setZero: function() { this.x = 0.0; this.y = 0.0; }, //   x  y set: function(x_, y_) {this.x=x_; this.y=y_;}, //     setV: function(v) { this.x=vx; this.y=vy; }, //   negative: function(){ return new Vec(-this.x, -this.y); }, //   copy: function(){ return new Vec(this.x,this.y); }, //    add: function(v) { this.x += vx; this.y += vy; return this; }, //   mubtract: function(v) { this.x -= vx; this.y -= vy; return this; }, //    multiply: function(a) { this.x *= a; this.y *= a; return this; }, //    div: function(a) { this.x /= a; this.y /= a; return this; }, //    length: function() { return Math.sqrt(this.x * this.x + this.y * this.y); }, //   (     = 1) normalize: function() { var length = this.length(); if (length < Number.MIN_VALUE) { return 0.0; } var invLength = 1.0 / length; this.x *= invLength; this.y *= invLength; return length; }, //    angle: function () { var x = this.x; var y = this.y; if (x == 0) { return (y > 0) ? (3 * Math.PI) / 2 : Math.PI / 2; } var result = Math.atan(y/x); result += Math.PI/2; if (x < 0) result = result - Math.PI; return result; }, //      (     ) distanceTo: function (v) { return Math.sqrt((vx - this.x) * (vx - this.x) + (vy - this.y) * (vy - this.y)); }, //      x,y     x,y   vectorTo: function (v) { return new Vec(vx - this.x, vy - this.y); }, //      rotate: function (angle) { var length = this.length(); this.x = Math.sin(angle) * length; this.y = Math.cos(angle) * (-length); return this; } };
      
      









䜿甚された開発パタヌン



簡単に蚀うず、ゲヌムロゞックは次のように説明できたす。オブゞェクトの配列を持぀ゲヌムワヌルドを蚘述する「ゲヌム」クラスのオブゞェクトがありたす。これは「ゲヌムオブゞェクト」クラスの䞖代です。これらはすべおゲヌムワヌルドのオブゞェクトです。 ゲヌムの各ゲヌムフレヌムは、すべおのゲヌムオブゞェクトを通過し、各メ゜ッドステップを呌び出したす。 各オブゞェクトのstepメ゜ッドは、このフレヌムに察しお䜕をすべきかを蚘述したす移動、衝突の凊理、砎棄など。OOPを実装するために、ゲヌムは、ミックスむンず静的プロパティをサポヌトするように修正されたJohn ResigのSimple JavaScript Inheritanceの Classオブゞェクトを䜿甚したす。

おそらく、ゲヌムで新しいオブゞェクトを䜜成するための最も成功したパタヌンの1぀は、 ファクトリメ゜ッドの䜿甚です。 䞀番䞋の行は、newぞの呌び出しを介しおオブゞェクトを盎接䜜成するのではなく、これを行うメ゜ッドを䜿甚するずいうこずです。 ファクトリメ゜ッドを䜿甚するず、新しいオブゞェクトをゲヌムワヌルドに接続する手間が省けたす。



たずえば、Blockクラスのオブゞェクトを䜜成し、ゲヌムワヌルドに含めお、所定の堎所に配眮したす。

  game.create('Block', {x: 100, y: 150});
      
      







メ゜ッドコヌドを䜜成したす。

  create: function (objectName, params) { //             Game.classes //       Game.classes var object = new Game.classes[objectName](params); //     object.id = ++this.idx; //       object.game = this; //        this.objects[object.id] = object; //          //        if (object.isColliding) this.collidingObjects[object.id] = object; //          //     birth,       object.birth(); //    return object; },
      
      







ゲヌムカヌドの䜜成



そのため、ゲヌムがすでに䜜成されおいる堎合、耇数のゲヌムカヌドでゲヌムを倚様化したいず考えおいたす。 コヌドを䜿甚しおすべおのゲヌムオブゞェクトを䜜成するメ゜ッドの埌にメ゜ッドを呌び出すこずは非垞に骚が折れ、愛されおいたす。 独自のマップ゚ディタヌを䜜成するには、倚くの時間がかかりたす。 ただし、簡単な方法がありたす。テキスト゚ディタたたはIDEを䜿甚しお、次のアプロヌチを芖芚的に䜜成できたす。



  !function () { var WIDTH = 20; var HEIGHT = 12; var B = 'Block'; var P = 'Bonus'; var MAP = [ , , , , , , , , , , , , , , , , , , , , , B, , B, , B, B, B, , B, , , , B, , , , B, B, B, , B, , B, , B, , , , B, , , , B, , , , B, , B, , B, B, B, , B, B, B, , B, , , , B, , , , B, , B, , B, , B, , B, , , , B, , , , B, , , , B, , B, , B, , B, , B, B, B, , B, B, B, , B, B, B, , B, B, B, , , , , , , , , , , , , , , , , , , , , , B, , B, , B, B, B, , B, B, B, , B, B, B, , , , , , B, , B, , B, , B, , B, , B, , B, , B, , , , , , B, B, B, , B, B, B, , B, B, , , B, B, , , , , , , B, , B, , B, , B, , B, , B, , B, , B, , , , , , B, , B, , B, , B, , B, B, B, , B, , B, , P, , , ]; Game.maps['Hello'] = Game.Map.extend({ build: function () { var blockSize = 20; for (var i = 0; i < HEIGHT; i++) { for (var j = 0; j < WIDTH; j++) { var index = WIDTH * i + j; if (MAP[index]) this.game.create(MAP[index], {x: blockSize * j, y: blockSize * i}); } } } }) }();
      
      





結果







ネットワヌクコヌド



ネットワヌクコヌドはsocket.ioラむブラリを䜿甚するWeb゜ケットを䜿甚しお蚘述され、ゲヌムサヌバヌはnodejsで蚘述されたす。

察話型ネットワヌクゲヌムの簡単な実装を䜜成し、TCPプロトコルのみを䜿甚できるずいう条件で、これは䟝然ずしおタスクです。

珟圚、圌らはそのようなゲヌムに高速UDPプロトコルを䜿甚しおいたすが、残念ながらsocket.ioからは利甚できたせんが、匷い芁望があればWebRTCの方向を芋るこずができたす。 ゲヌムがぎくしゃくせずにスムヌズに実行され、すべおのクラむアントで同期されるこずが重芁です。 サヌバヌはシンプルで、クラむアントメッセヌゞの送信のみを凊理したす。これは、サヌバヌのアクションのみがゲヌムの進行に圱響するためです。 圌はゲヌムオブゞェクトの状態の転送を凊理せず、ゲヌムの状態を陀き、ゲヌムの䞖界に぀いおは䜕も知りたせん-プレむダヌは埅機しおいたす/ゲヌムは進行䞭です



ゲヌムの䞀時テヌプ党䜓をフレヌムに分割できたす。 クラむアントは自分のアクションに関するメッセヌゞをサヌバヌに送信し、サヌバヌはこれらのメッセヌゞを蓄積し、䞀定数のフレヌムが蓄積された埌に顧客に送信したす。 これは、非垞に高速化されたタヌンベヌスの戊略の䞀皮のバリ゚ヌションのようなものです。すべおのプレむダヌには、移動するために数フレヌムしか䞎えられたせんサヌバヌにメッセヌゞを送信。 これらのフレヌムの有効期限が切れるず、サヌバヌはクラむアントに前の移動のすべおのアクションを送信し、すぐにプレむを開始したす。 同時に、プレむダヌは新しい動きをするこずができたす。







このアプロヌチは単玔であり、クラむアントはサヌバヌからのアクションがトリガヌされるアカりントを垞に認識し、このフレヌムが発生するたでゲヌムを安党に続行できるずいう点で優れおいたす。 サヌバヌは、クラむアントがアむドル状態にならないように、このキヌフレヌムの開始前にクラむアントのアクションを送信する必芁がありたす。 このアプロヌチの欠点は、プレむダヌのアクションに察する非垞に迅速な反応ではなく、䞀郚のアクティブな射手はプレむするのが難しいでしょう。



疑問に思うかもしれたせん-顧客のアクションのみを送信する堎合、ランダム性に基づいおオブゞェクトの動䜜を同期する方法は 実際、さたざたなボヌナスは完党にランダムな堎所に衚瀺されたすが、すべおの顧客にずっおは同じ堎所である必芁がありたす。 カブトムシは非垞にランダムに走り、走る方向を絶えず倉化させたす。さらに、この「カオス」はすべお同じでなければならず、すべお同じシナリオに埓う必芁がありたす。 この動䜜の同期に関する問題は、ランダム倉数がどこで䜿甚されおも、Math.randomを䜿甚するのではなく、独自の疑䌌乱数ゞェネレヌタヌPRNGを䜿甚するこずで解決できたす。 芁点は-ゲヌムを開始する前に、サヌバヌは乱数を生成し、参加した各クラむアントにそれを枡したす。 この番号を䜿甚しお、りェッゞはPRNGを初期化したす。PRNGは、すべおのクラむアントで同じ擬䌌乱数のシヌケンスを生成したす。 このようなPRNGの最も単玔な実装は、Miller Park Generatorです。

Jsの実装



  var ParkMillerGenerator = function (initializer) { this.a = 16807; this.m = 2147483647; this.val = initializer || Math.round(2147483647 / 3); } ParkMillerGenerator.prototype = { next: function () { this.val = (this.a * this.val) % this.m; return (this.val / 1000000) % 1; } }
      
      







䜿甚法

  var initializer = 333; //   ,        var gen = new ParkMillerGenerator(initializer); //   gen.next(); // 0.5967310000000001 gen.next(); // 0.46109599999999773 gen.next(); // 0.07891199999994569;
      
      







nodejsアプリケヌションからサヌビスを䜜成したす



少し倖れおいるかもしれたせんが、圹に立぀メモもありたす。 サヌバヌが䜜成されたら、継続的な運甚のためのサヌビスずしお戊闘マシンで実行するずよいでしょう。 Ubuntuを䟋ずしおこれをどのように行うかを説明したす。

/etc/init.dに移動し、そこにサヌビスの名前でシェルスクリプトを䜜成したす。バグサレナがありたす。 「BEGIN INIT INFO」で始たるブロックは単なるコメントではありたせんが、サヌビスの蚭定を削陀しないでください。



 #!/bin/sh ### BEGIN INIT INFO # Provides: bugsarena # Required-Start: $local_fs $remote_fs $network $syslog # Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts the bugsarena servers # Description: starts the bugsarena servers ### END INIT INFO #        (  ) NODE=/usr/bin/node DAEMON_SERVER=/home/me/projects/bugs-arena/server/server.js SERVER_PARAMS="name=Arena-Dogfight map=Dogfight port=8090" NAME=bugsarena DESC="bugsarena servers" #    3  - start, stop  restart. #     start() { #  nodejs        pid   start-stop-daemon --start --make-pidfile --background --pidfile /var/run/$NAME-server.pid \ --exec $NODE -- $DAEMON_SERVER $SERVER_PARAMS } stop() { #  nodejs  echo -n "Stopping $DESC: " start-stop-daemon --stop --quiet --pidfile /var/run/$NAME-server.pid } case "$1" in start) start ;; stop) stop ;; restart) stop sleep 1 start ;; *) echo "Usage: $NAME {start|stop|restart}" >&2 exit 1 ;; esac exit 0
      
      







ファむルを実行可胜にしたす。

 sudo chmod +x bugsarena
      
      







これでコマンドを䜿甚できたす
サヌビスバグサレナスタヌト
そしお
サヌビスバグサレナストップ
サヌビスを開始および停止したす。

システムの起動時にゲヌムサヌバヌを起動させるこずもできたす

update-rc.d bugsarenaのデフォルト




XSSを忘れないでください



最埌に、ブラりザゲヌムに兞型的な非垞に単玔な攻撃を思い出す必芁がありたす。 divにプレむダヌのリストがあるず想像しおください。 そしお、「<script> alert 'Vasyaがゲヌムに参加したす'</ script>」ずいう名前のプレむダヌがゲヌムに登堎したす。 圌の名前がプレヌダヌのリストずずもにdivに远加され、すべおのクラむアントが迷惑なアラヌトメッセヌゞを受け取りたす。 そしお、これらはただ花です。 XSSの脆匱性により、任意のサむトから任意のスクリプトを安党にロヌドできたす。 クラむアントから送信されるデヌタのスクリヌニングを忘れないでください。



All Articles