「転送」を含む日記 RSS

はてなキーワード: 転送とは

2019-03-25

Stadia開発者インタビュー その3

ブツブツ切れる・・・

Youtubeマクロミクロレベルで数多くのネットワーク状況に対する情報提供してくれます。我々はそれらの情報も利用可能ゲーマー体験を調整、拡大が可能です。

しかユーザYouTubeの画質をゲーム内の品質だと思ってはいけませんよね?

その通りです。StadiaをYoutubeとは異ならせる2つの事象がありますYouTubeバッファ使用可能リアルタイムでもなく、ゲームのようにインタラクティブでもありません。Stadiaはバッファリングができません。Stadiaはフレーム数が絶対的に正確である必要があります

次に2つ目ですが、ユーザDoomやAssasin's Creedレベルの画質のゲームを遊ぶ場合、その期待はYouTube向けにユーザ作成したコンテンツよりもずっと高くなります

さらにもう1つお話する価値がある点として、YouTubeとStadiaの繋がりについて今後も多くのことを話し、デモをしていきますが、大前提としてゲームとはリンクであり、リンクは無数の方法で配布、探索、共有が可能であることが挙げられます

すると例えば土曜の夜10時にDoom Eternalを遊ぶ計画をしたとして、Webページボタンを押すだけで我々を貴方方に加えることが可能だと言えますか?

その通り、全く仰る通りです。あなたが仰った通りのシナリオになります。また例えばEurogamerが新しいゲームレビューしているとしたら、ユーザレビュー記事クリックするだけでダウンロードすることもなく、パッチを当てることもなく、インストールすることもなしに簡単にそのゲームを試すことが可能です。また他にはあなた記事が新しいマップキャラクターレベルについて記述している場合でもそうです。我々にはState Share(状態共有)と呼ばれる既存技術と一緒に働くとても強力な新技術を用意しています

State Shareは本当にとても強力な機能ユーザが最新のバージョンゲームを遊んでいる時に、同時に友人や私、または世界中にそのゲームストリーミング可能です。誰が相手でも問題ありません。またユーザゲーム内での最新の武器、例えばDoomのFlaming Swordや、とにかくそゲームで最高のアイテムを入手したとします。そして私が「カッコイイ! 私もそのFlaming Swordが欲しい!」と考えたとしましょう。私がそのビデオクリックすればそのメタデータがそれら全ての属性を直接私のゲームにも転送します。だから誰でもが他人ビデオストリームに飛び込めるだけでなく、その他人の状況全てと共にゲームに飛び込めるのです。これは開発者ゲームが設定可能です。これについては他にも例がありますが、1つはユーザゲーム特定の難しい状況に陥った場合に、ユーザコミュニティにその状況を解決できるか試してもらうことが可能です。全く同じ状況で渡すことができます

我々がYoutubeとの接続で行っている別の例は、典型的シナリオとして、私がパズルベースアドベンチャーゲームをやっている場合によくある状況があります。そのゲームのあるポイントで立ち往生してしまいました。その特定パズル、例えばトゥームレーダーのある墓や何かができないとしましょう。現状では電話を取り上げるかラップトップに向かいヒントをダウンロードするでしょう。それかYoutubeに向かって動画を探します。しかしStadiaではボタンを押して「Hey, Stadia. このボスはどうやってやっつければ良いんだい?」と聞きます。するとStadiaがYoutube動画を探してきてゲーム内で再生します。私はレベルの解答を見て続けることができます

先程、申しましたゲームとはリンクであるとの点ですが、任意Webサイトリンクを持つことができますDiscordFacebookTwitter電子メールテキストメッセージWhatsAppGoogle検索結果、さらにはGoogle Play Storeですらゲームの配布が可能になります

それはどのように動いていますか?

リンクです。

でも基本的に分離されたシステムですよね?

そうです。インターネット全体に渡り分散された広大な領域の利点を得ています。我々は開発者パブリッシャに対し、インターネットストレージであるとのコンセプトを広めています。もちろんStadiaは専用のストレージを持っていますしかし我々は開発者に対しゲームを自社のコミュニティへ持っていくことも、例えどこにコミュニティがあろうとも許可します。このことが配布と探索を開発者にとってとても良い意味で逆さにします。また我々にはとても高速なマーケティング技術がありますので、パブリッシャは体験をできる限り多くの人々にとても効率の良い手段で配布することが可能です。


もっと全体的なレベルで、抜本的にStadiaはYoutube統合しているのですね。ではプラットフォーム上の全てをどうやってモデレートしますか?

コミュニティに対するモデレーションにはとても強固なアプローチを取りますYouTubeは途方もない投資をこの領域に行ってきました。我々はそこに提携をし、さらに家庭レベルでも行います。我々はこの業界でも最も優れたペアレンタルコントロールゲーマー健康コントロール提供します。両親は子供が何を遊ぶか、誰と遊ぶか、いつ遊ぶかを管理可能です。

その点に関してはデータプライバシも気になります。それらは全て既存Googleインフラに紐付けられるのですか?

我々は全てをユーザ制御下に置くことを使命としていますGoogle提供する制御機能と同じレベルのものがStadiaでも提供されるとお考え下さい。

ユーザサイドでゲームを行うことやこれまでのコンソール文化は滅ぶでしょうか?

それは私共が答えることではありません。Stadiaはゲームの新しい開発の仕方、ゲームの新しい探し方、ゲームの新しい楽しみ方を提供します。我々には大きな未来への志があります。大規模に発展したいと考えています。それは一晩で起こることではありません。また我々はこれまでに存在し、我々をここまで導いて下さった物、全てを尊敬しています。私達は業界全体が成功し、成長することを望んでいます

Stadiaの開発者インタビュー その2

やっぱり途中で切れたので続きから


サービス提供を開始します。

するとシステムの準備は万端で、必要ソフトウェアの開発が必要ということでしょうか。Microsoftを見ますと、彼等はXBox OneのHWをサーバラックに積んでいるようです。あなたがたの手法とは異なりますか?

違います

するとGoogleインフラバックエンドのみでなくソフトウェアにも投資を考えていますか?

そうです。

自身の開発スタジオも考えていますか?

はい。Stadia Games and Entertainment組織を発表しました。これは我々の1stパーティスタジオです。

それが今起こっている?

はい

Google開発者に対し全てのツール支援しています。Stadia向けの開発は彼らにとって別のターゲットしか過ぎません。Visual Studioを用いる既存ツールや彼らが用いるツールの全てと共に、彼らのワークフロー統合されます。従ってStadia向けの開発はPlayStationXbox向けの開発と同じくらい簡単です。

我々はUnrealサポートします。UnityがStadiaサポートします。予想される多種多用な業界標準ツールミドルウェアが準備されます

意地悪な質問します。Googleコントロールを超えたものがありますよね。特にユーザサイド、クライアントサイドのインフラ家庭内安価ルータです。このような問題をどのように解決しますか?

とても良い質問です。我々はユーザに対し彼等のインフラの中で何が起こっているかをできる限り理解できるよう支援する必要があります。また我々はゲーマーに対し最適な体験を得られるようなチューニングを行うことが可能情報に対し投資を行うだけでなく、我々自身技術を用いて最良のパフォーマンスを実現するつもりです。Google技術の多くがインターネット網の基盤であることを思い出して下さい。我々はDCから情報がどのようにユーザに届くかを良く理解しております。できる限りの最適化を行うつもりです。

デベロッパパブリッシャは既存ゲームをStadia移植できるのですね。しかし同時にGoogleは新しい選択肢も数多く提供できると

その通りです。それこそが我々のプラットフォーム根本的な差別化ポイントです。既存ゲームカタログを持つデベロッパにとってStadia簡単で親しみ易いものです。我々はできる限り摩擦なくゲーム移植を行えるようにします。なぜならゲーマーは好きなゲームを遊びたいですし、彼らの愛するキャラクターストーリー世界を楽しみたいのです。しかし我々はまた開発者未来を描く新しいキャンバスをも提供します。ゲームを高速に配布し、プレーヤーと新しい手段で、特にYoutubeにて繋げます。そして開発者が持つアイデアを実現するための前例の無い技術提供します。


これまでのクラウドシステムクライアントサイドの限界基本的に画質やレイテンシに起こりました。明らかにこれらの問題インパクトはここ数年で新たにより良い技術を得ることとインフラ改善されることで緩和されてきました。しかローカル品質ストリーミング品質との間には今でも根本的なギャップ存在します。私はidソフトウェアZenimax特許を見たのですが彼らはh.264のモーションベクター効果的に用いてクライアントサイドのある程度の予測を立てレイテンシの知覚を減らしていました。Project Streamにも測定可能レイテンシ存在します。ギャップを閉じるために何をしていますか? これらの問題解決されたでしょうか。それとも少なくとも緩和はされましたか

解決されたと同時に緩和されています。まずデータセンターに対しより多くの人々がより良い経験を得られるようにするための投資が行われました。また圧縮アルゴリズムについては我々に抜本的な先進性が存在します。Google圧縮アルゴリズム標準仕様先駆者でありこの点がストリーミングの将来をより確実にします。残念なことですがGoogleでも制御できない点が光の速さです。そのためこの点が常に要因となりますしかし常に理解しなければならないこととして、我々は常にエッジ(終端)にもインフラを構築していることが挙げられますGoogleの中心にある巨大なデータセンタだけではありません。我々はできる限りエンドユーザの側にインフラを構築しています。それによって歴史上の幾つかの問題回避することが可能です。さらにまだ率直な、あまり洗練されていないProject Streamのストリーマーでも信じられない結果を出していますさらに我々はサービスリリース時に1080p60を超える品質を実現できるだけの根本的な改善を行いました。我々は8Kに至るでしょう。

それは素晴しい。それらの改善は全て圧縮に関連するものですか?

圧縮ネットワークです。我々はGoogleインフラに投入した数々の改善点に依っています。BBR、QUIC、WebRTCを基盤としてその上に構築がなされました。だからIPパケットの低レイテンシでの配信だけでなく、送信元へのフィードバックも行うことが可能です。ですのであなたが仰るZenimax使用した技術なら、彼らはここでも利用することが可能です。彼らは彼らのゲーム最適化を行うことができるでしょう。我々はフレーム毎のレイテンシ予測可能で彼らにそれに合わせて調整を行わせることができます


入力を受け取って、ゲームロジックを処理して描画を行うと、60Hzのゲームでは50ms程になります。続いてエンコード転送デコード、表示を行うとストリームではPC上のゲームに比べさらに60ms程かかります。これを改善することはできますか?

我々は改善を続けますStream最初バージョンです。我々は性能向上のために調査を行っており、レイテンシ適応していきますリリース時にはより良くなっているでしょう。

クラウドシステム接続可能性に従って成否が決まります。例えばRed Dead Redemption 2を数万、数十万、場合によっては数百万のプレーヤーオンラインにて同時に立ち上げるとします。システムの成否はアクセスによります。もしゲームプレイできないなら重大な障害となります

かにそのとおりです。そしてそれこそがGoogleが何年もかけて開発してきたスキルであり、抜本的なスケールする能力です。我々がどうやって実現しているのか、何をしてきたかについては今日は詳細にはお話しません。しかGMailMapYoutubeが同時に利用可能であるためと同じ基本的技術のいくつかが我々が依るものです。


我々は現在、現行世代が終了する移行の時を迎えています。これまではコンソールベースラインを定めてきました。Stadia次世代XboxPlayStationに対抗できると考えますか?

我々は競合他社が何をしているかは存じておりません。

でもGoogleには良い予測を立てられる優れた人々がいますよね?

我々の第一世代システムに導入されるGPU10Tflops以上の性能があり、さらスケールアップしま

GPUユーザ間で共有されますか? それとも1人のユーザが独占しますか?

単一インスタンスです。

NVIDIAGPUですか?

AMDです。

カスタムGPUだと思います

カスタムGPUです。

Google要件のために作られた訳ですね。他にも公開できる情報はありますか? Vegaですか? それともNavi、もしくはさら新世代ですか?

情報を公開したくない訳ではないのですが、このプラットフォーム進化することのほうがより重要です。そしてこの進化ユーザ開発者の双方に対しシームレスに行われることを確認して頂きたいのです。そして進化は常に継続し、誰もが常に最新で最高の物を手に入れます

クラウド確約する点ですね。ユーザはHWをアップグレードする必要がないと。

開発者にもこのように考えて欲しいのです。もちろん完全には抽象化されていません。特にゲーム開発者にとっては。しかし我々はそれでもこのプラットフォームが常に進化していると考えて欲しいのです。速さや容量、リソースには制限されていないのだと。

AMDGPUを使うことでStadiaと他のコンソールの間に共通な点ができました。開発者にとって利益となるでしょうか?

シェーダコンパイラツールをいくつか開発しました。これらは開発を楽にするでしょう。しか現在GPUはとても優れており開発者が既にVulkanに親しんでいれば、例えばid Softwareさんは既に全てVulkanに移行していますが、そのような開発者の方々には既存ゲームをStadia移植するのはとても簡単です。Doom Eternal4K、60フレームで動いでいるのは既にご覧になったと思います。非常に素晴しい状態です。これこそが我々にとって重要証明ポイントです。FPSグラフィックプレイアビリティの双方で要求が高いゲームです。 従ってこれは我々のプラットフォームの強力な証拠であり、idさんにも講演して頂きます

CPUについては何か公開できる話はありますか?

x86で2.7GHzで動作しています開発者にとり慣れのあるものです。開発全体を通して、CPUは制約となる要因ではありません。我々は全てのタイトル動作するに十分なCPU提供します。

コア数、スレッド数は?

沢山です。

8コア、16スレッド? それより多い? 少い?

公開できない情報です。ハイパースレッド使用しています

しかサーバ級のCPUです。Stadiaはこれまでのコンソールと違いパッケージングに制約を受けません。熱対策問題も異なりますコンソールとはサイズパッケージング動機が異なりますデータセンタの中でそれはとても汚なく見えるかもしれません。一方でとても帯域幅が高いメモリ使用可能で、とても高速なペタバイト級のローカルストレージ使用可能です。ご家庭のコンシューマデバイスよりも数百倍は速い物です。

するとそれが開発者が全く異なる体験のために利用可能な要因となる訳ですね。得られる機会はとても大きいことでしょう。しかし我々はマルチプラットフォーム時代生活していますGoogle先進的な機能は1stパーティのみが利用できるものですか?

パートナーには彼らが話せる時点で彼らの計画を教えてくれるよう伝えています。Stadiaをこの世界で最も偉大なゲーム開発者達に説明することにはとても興奮します。Stadiaは、開発コードではYetiと呼ばれていましたが、Stadiaビジョン説明すると、開発者リアクションは「これは私が期待したもののものだ。これはまさに我々の次のゲームのためのビジョンのものだ。elastic computingの考え、次世代レベルマルチプレーヤー環境ゲームを観ることと遊ぶことの境をあやふやにし1つの体験にすること」と話されます

イノベーションの1つの領域として、最初のほうで述べましたが、マルチプレーヤー環境において、単純にパケット複数プレーヤーリダイレクすることから原子時計レベルでのコンシステンシーを全ての状態遷移において定期的にクライアント間で更新する真のシミュレーションへの移行が挙げられます。これにより開発者はこれまでには不可能だった分散された物理シミュレーションを得ることができます。これだけでもゲーム設計イノベーションに対し大きく寄与します。このため多くの開発者が、大袈裟でなしに、実際にとても感動的なリアクションを我々のプレゼンに対して返して下さっています


多くの問題解決し、規準を上げる可能性がある訳ですね。

これこそがゲーム業界の素晴しい点です。技術が常に創造性を刺激し、ゲームに対しより大きな聴衆を作り、そのことがプレーヤー開発者に対しより大きな機会を作ってきました。エコシステム進化し、正の方向に回り続けるなら、それはゲームを遊ぶことにとって良いことです。

あなたは先程、スケーラビティとStadiaがどのようにしてより多くのリソースを立ち上げ増大する要求対応するかについて話して下さいました。しかし、同時に10TflopsのGPUサーバクラスCPU存在するとも仰いました。リソース拡張をどのように行うのですか?

3台のGPSが一緒に実行されるデモを行っています。私は上限が無いとは申しません。しかし我々は技術上の限界を上げています。そしてStadiaは静的なプラットフォームではありません。このプラットフォームは5年や6年の間、レベルが変わらない訳ではありません。開発者プレーヤー要求に従い、成長し、進化するプラットフォームです。なぜならStadiaデータセンタの中に構築されています進化させるのは我々にとって簡単なことです。

ここまで第一世代について多くのことを教えて頂きました。すると第二世代やその後の世代もあるでしょう。現状でもスケールアップできるわけですが、3台のGPU連携次世代でも可能ですか?

CPU/GPU/メモリ帯域幅の変更にはいくつかの自然な段階があります。これは家庭の物理な小売の端末よりももっとスムースでより継続的な進化です。しかし、より重要なことは基盤データセンタ網とそれに含まれネットワーク技術への投資です。この2つが一致して行われることが重要でどちらか1つではダメなのです。

先程、エッジ上のインフラについて触れられましたが、例えばNetflixISPキャッシュインストールしている状況があると思います。これがGoogleが行っていることですか? それとも既にYoutubeで行われていますか?

それは我々も既に行っていますGoogleが既に20年以上、行っていることです。我々が依って立つまた別の巨人の肩です。


Project Streamではユーザに対し最低で25Mbpsの帯域幅要件しました。これはストリーミングのみのためですか? それとも他のユーザが同時に同じインスタンス接続する場合を含みますか?

我々はStreamさらに強化させています。従ってユーザはこの制約が全体のスタックに対する改善最適化、また特に時間によって緩和されることを期待するでしょう。我々はその期待の上を行きます

4K60には相当の帯域幅がいるのでしょうね?

4K60はもちろんより要求が高くなります

1080p60は低くなる・・・

私は具体的な数値についてはコメントしません。しかし当然低くなります

インターネット接続環境はStadiaリリースする市場では全体的に上昇機運が見られます。つまりこのパフォーマンス特性ますます多くのユーザが利用可能になります

さらに繰り返しになりますが、BBRを初めとする我々の技術がありますさらに覚えておいて頂きたいのは我々のネットワークに対する理解そのままではありません。それらもまた時と共に改善されていきますYoutubeマクロ

Stadiaの開発者インタビュー

Eurogamerにより独占配信されたStadia開発者二人に対するインタビュー記事

https://www.eurogamer.net/articles/digitalfoundry-2019-google-stadia-phil-harrison-majd-bakar-interview

やっつけなので可能なら原文を読むことをお勧めします。

---

なぜ今なのでしょうか?

タイミング問題です。20年間の蓄積によりGoogleにはデータセンタ内のパフォーマンスに優位性が存在します。Googleデータセンタ内ではHWメーカーです。我々はデータセンタ内で何年もの間、高い性能で端末間を接続する基盤を構築してきました。Youtubeでの経験からプレーヤーサイドの観点からだけでなくデータセンタ内部から技術観点から技術統合を行ってきました。他社でもその視点存在していますGoogleにはその点に固有のアドバンテージ存在します。

これまでの箱をTVの下に置いておいたパラダイムに比べ、無限演算リソースによる可能性が現れます。これまで存在しなかったことをできる可能性があります

その通りです。我々にはレガシーがありません。全てが21世紀のために設計されています開発者制限の無い計算資源が利用でき、何よりもマルチプレーヤーサポートできます。これまでのマルチプレーヤー環境は一番遅い通信に影響を受け開発者は最も遅い接続に対し最適化必要でした。我々のプラットフォームではクライアントサーバも同じアーキテクチャの下にあります。これまではクライアントサーバの間のping支配されていましたが我々の環境なら最速でマイクロ秒で済みます。だからプレーヤーの数は単一インスタンスにて動的にスケールアップが可能です。バトルロイヤルなら数百から数千、数万のプレーヤーが集まることも可能です。それが実際に楽しいかどうかは置いておくとしても、新聞ヘッドラインを飾ることが可能技術です。

クライアントサーバの双方でこの利益を得られるのでしょうか

両方です。

すると開発者に対しStadiaホリデーシーズンにぴったりの最高の製品だと言えると。理に適った範囲無限計算資源が得られると。

ユーザが我々のプライベートLANからはみ出さないだけでもその効果は大きいものです。Googleは45万kmに及ぶ光ケーブルにより世界中データセンタ間を接続しています米国西海岸から東海岸まででも20ms、フランクフルトからマドリッドでも20ms。これにより開発者は最も極端な場合においてもレイテンシ予測可能でそれに従い設計を行うことができます


Youtbeとの統合について教えて下さい。

StadiaYoutube技術と深く結びついていますが、実際には一歩引いています今日ゲーム業界を考えてみて下さい。2つの世界共存しています。1つはゲームプレイする人々で、もう1つはゲームを見る人達です。2億人の人々がYoutubeゲーム毎日見ています2018年には述べで500億時間ゲームを視聴するのに費されています時間人口の双方で信じられない程の視聴が存在します。我々のビジョンはこの2つの世界を1つにすることでゲームを見ることができ、かつ、プレイもできる、双方向に楽しめることです。

まり重要なのはゲームシステムでもなくコンソールでもありません。噂とは異なり我々はコンソールビジネスには参入しません。我々のプラットフォームの要点はコンソールでは無いことで、皆が集まる場所を作ることです。我々は箱でなく場所を作る。今までと異なる体験を得られる場所です。ゲームを見るなり、遊ぶなり、参加する場所であり、かつユーザが楽しむ場所であり、ユーザ他人を楽しませる場所です。

から我々のブランドはStadiaといいます。これはスタジアム複数形です。スタジアムスポーツを行う場所ですが同時に誰もがエンターテイメントを楽しむ場所でもあります。だから我々はそれをブランドにしたかったのです。皆が遊んで、観て、参加して、さらにはゲームをする場所。一歩下がって見ることもできる場所。常にどのボタンを押したか意識しないでも良い場所。他のアーキテクチャでは実現できない場所です。

まりリアルタイムシミュレーションゲームで全ての駒が人々であるようなものですか?

その通りです。そして単純に技術的に深い点を求めて、我々は第一世代でも4K60fpsHDRサラウンドサポートしました。さら開発者必要インフラに従ってスケールします。それだけでなく、同時にYoutubeに常に4K60fpsHDR画像送信することが可能です。だからあなたゲーム体験の思い出は常に最高の状態になります

Googleは全てを記録するでしょうか?

プレーヤー次第です。Googleは全てを記録はしません。もしプレーヤーが望むならGoogle4Kストリームしま

共有が友達だけか、世界中に公開かも自由選択可能です。Googleユーザ制御を明け渡します。もしユーザYoutubeで公開すれば誰でもリンククリックすることでそのゲームを遊ぶことができます

するとユーザshareするだけで誰でもがその特定ゲームに参加することができる訳ですね。

そう。そしてこれはマルチプレーヤーゲームロビーの新しい形となりますYoutubeクリエイターなら誰でもがファンチャンネルのsubscriberを自分ゲームへと誘うことができます生主として、Youtubeクリエイターとして私は視聴者を私のゲームに瞬間的に招待できます。それが私と10人の友達でも、(訳注: セレブの)Matpatと彼の数百万の購読者でも、技術は同じです。

アカウントシステムベースYoutubeですか?

Googleアカウントの一部です。従ってGMailアカウントがStadiaへのログインに利用できます。他の基盤についても説明させて下さい。最初サービス立ち上げから全ての画面への対応を行いますTVPCラップトップタブレット携帯です。我々のプラットフォームの基本は画面に依存しないことです。これまで40年間、ゲーム開発は端末依存でした。開発者として私は制約の範囲内で、私の創造性を開発対象の端末に合わせてスケールダウンする必要がありました。

我々はStadiaでそれを逆にしたいのです。我々は開発者に対し彼らの考えをスケールさせ、どの端末の縛りから解放したいのです。パフォーマンスに優れ、リンククリックすればゲームは5秒以内に開始されますダウンロードもなく、パッチもなく、インストール必要なく、アップデートもありません。多くの場合、専用のHWも必要がありません。従って古いラップトップChromeブラウザ使用する場合にでも皆さんが既に持っているだろうHID仕様に準ずるUSBコントローラ動作します。そして、もちろん、我々自身コントローラも開発中です。

なぜ独自コントローラを作るのですか? USBコントローラはどこにでもあるじゃないですか

コントローラ自作する理由はいくつかあります。1つはTVへの接続です。我々はChromecastをストリーミング技術採用します。Stadiaコントローラの最も優れた機能の1つはそれがWiFi接続DC内のゲームに直接接続することです。ローカルデバイスとは接続しません。

それは面白い。するとほとんどそれ自体が端末な訳ですね。

その通りです。これこそが我々のブランドの実現であり、具現化です。そして独自コントローラにより最高のパフォーマンスが実現します。ゲームに直接接続するためにプレーヤーは画面を移動することが可能です。プレーヤーはどの画面でも自由に遊び、停止し、他の画面でゲームに復帰することが可能です。

そしてコントローラには2つの追加されたボタンがあります。1つはGoogle Assistantの技術マイクを用いますユーザ選択により、ユーザプラットフォームゲームの双方に対し、自然言語を用いて会話が可能です。例えば「Hey, Google。MadjとPatrickと一緒にGame Xをやりたいな」と言えばStadiaマルチプレーヤーゲーム指定した友人と共に直ぐに開始します。

するとGoogle伝統的なUI回避するのですね?

我々はゲーマー可能な限り素早くゲームに辿り着かせるよう考えています。数多くの研究を行いましたが、多くのゲーマーゲームを起動したら直ぐに友人とゲームを開始したいと考えていますゲーマーUI時間を費したくは無いのです。

誰かが言ったことですが、現在コンソールは起動した時にまるで仕事のように感じると言うのです。ゲーム自体更新や、ゲーム更新があります。我々はそれらを完全に取り除きたいと考えています。もう1つのボタンは、ちょっと趣が異なるのですが、Youtubeシェアできます

端末は何でも良いのですね? スマホスマートTVも?

Youtubeが観られるならどこでもStadiaは動きます

TVへの接続にはChromecastが使用されると。では実際にはどのように動きますか?Chromecastがスマホラップトップからストリーミングを受け取るのでしょうか?

Chromecastはスマホからストリームを受取はしません。Chromecastはスマホから命令のみ受けます画像NetflixYoutubeから直接受け取ります。Stadia場合、StadiaコントローラからChromecastへとこのゲームインスタンスへと接続せよと命令がなされ、Chromecastはゲームインスタンスから動画ストリームを受け取りますクライアントはとてもシンプルです。行うのはネットワーク接続ビデオと音声のデコードのみです。Chromecastは入力を処理しません。全て入力コントローラが扱いますビデオと音声とネットワーク接続Chromecastの基本動作で全て既に組込まれています

Stadiaの起動はどうするのですか? コントローラで?

そうです。とても良く出来ていますWiFiに繋ぐだけです。コントローラにはWiFiIDとPWを入れるだけです。それだけです。ホームボタンを押すと勝手Chromecastを探し直ぐにChromecast上でクライアントを起動します。UIが表示され直ぐにゲームを遊ぶことができますデジタルパッドでUI操作することも可能です。これが重い処理を全てクラウドへと移行する点の美しさです。Chromecastのような低消費電力の端末で説得力のある体験ができますChromecastは5W位下です。Micro-USBで給電可能です。典型的コンソール100から150Wもします。またこれまで説明しませんでしたが、例えスマホでも行うことは動画再生だけです。従ってAssassin's CreedDoomや他の重いゲームあなたスマホの上でモバイルゲームよりも低消費電力で動作します。だからスマホ10時間でも遊べます

スマートTVではStadiaYoutubeクライアントに組込まれるのでしょうか。それともStadia独立して起動させますか?

今の所、我々はChromecastのみに集中しています。でも技術的、機能的な観点からYoutubeがある場所ならどこでも動きます。我々はまだStadiaをどのようにユーザに届けるかは検討中です。

コントローラに話が戻りますが、モバイル端末にはやはり物理的なアタッチメント必要に思われます。例えばスマホコントローラ接続するような。MicrosoftのXCloudを見ていると操作には実際に問題があるようです。

Googleには解決手段があります

そうでしょう。スマホコントローラ取り付け以外にも、明らかな解決手段としてSwitchのようなクライアント端末を作るのでしょうか?

サービス開始時から提供されるサードパーティによる解決手段サポートしています。他にもアイデアがありますしかし今は話せません。

なるほど。GoogleUbisoftデモを行いましたが、これまでにDoom 2016でもデモを行いました。他にも開発企業はありますか?

良い質問です。私がこのプロジェクトに参加する前からチームは既に何社かと提携しここ何年かの間に技術提供していました。StadiaLinuxベースです。グラフィックAPIはVulkanです。開発企業クラウドインスタンス作成しますので、開発キットも今ではクラウドにありますしかクラウドだけでなく、開発社のプライベートDCでも、机上のPCでも可能です。

すると開発者物理的なHWを持つことが可能ですか?

もしそうしたいなら。でも我々は今後のトレンドが開発でも配布でもますますクラウドへと移行していくと考えています。従って今後数年で開発者にとってクラウド中心、クラウドネイティブゲーム開発での標準となるでしょう。

どの企業自身クラウドシステムを開発しているように見えます。例えばOriginクラウドがあるでしょう。しか必要とされるインフラ要件は、我々がここで話しているような内容を達成するには、3rdパーティには荷が重いように思えます。彼らは自身クラウド継続しながら、Googleシステムを導入するでしょうか。

デベロッパーパブリッシャーはとても賢くクラウドネイティブとなる新しいゲーム体験を達成するために必要ツール技術について考えていると思いますしかしそれは世界中で何千ものアクセスポイントを持つデータセンターを運営することや、それらの運営必要な莫大な投資資本とは異なるものです。Googleは今年単年でも$13Bの資本を投下しています

それはとても巨額ですね。しかしそれでも依然としてシステムを構築しインストールするのは根本的な問題です。多分野に渡る段階的なロールアウトになるのでしょうか?

米国では全ての必要場所に展開が終わっています。Project Stream試験必要環境2018年末には整いました。我々はGoogle社内で、Google社員対象2017年の始めから2年間の間、プライベートテストを行ってきました。2019年には米、加、西欧、英にて Permalink | 記事への反応(1) | 06:10

2019-03-21

家庭裁判所ホモセックス

酒の席だが職員のH氏から奇妙な話を聞いので書きとめておく。H氏はエロい。まずそのホモ室は家庭裁判所の中にあるらしいが結界により外から存在認識出来ない。ゴム無し。離婚裁判やら離婚調停で暴れる寸前までなった輩が問題起こす前に転送される。「え?!」と思わずH氏の発言に酔いが覚めた。更にそこではハッパも吸い放題であり合法特区めいているだとか。いやいやヤクは良くないよピエールだがだがコレコレH氏はエロい職員だ「だって離婚発狂する人てこうでもしないとイライラまらないでしょ?」精神科パートナーが悪いて騒いで終わり行かないよ絶対にwあと暴力ダメなんだよ、弱い方に来る圧力ダメなんだよ。いやいやいやヤクやってもね薬ダメダメ。という話が場末飲み屋の深夜まで続いた。

2019-03-17

Macよ、iTunesいい加減になんとかしろ

バージョンアップするたびクソになってくぞ

同期がクソでまともに転送しやしねえ

いつも追加する時は同期全消ししてから再同期してる

そうしないと全然同期してくれねえ

毎回500曲近い全同期してんだけどこれが5分くらいかかる

いい加減なんとかしろよチンカス

anond:20190314215624

多くの異世界生物神様が転生させてるから似たような場所に行くのは当たり前。

転送物も意思疎通できる奴を召喚しないと意味無いし。ただ転生物だと昔に地球から人間召喚した(それでいろいろ成功した)という前例がある場合も多いしわざわざ人間を狙って召喚してるとも言える

2019-03-07

一人会社やってんだけど、めったに電話なんか鳴らないか普段転送設定してなくて不在時は留守電だけしてるんだけど

おととい電話が同じ番号から5件くらいあって、たまたまいなくて受けれなくて気づいたの夜だったから翌日折り返したのね。2回くらい。

でも出てくれなくて、かかってこないし、まぁいいかと思ったらさ、今日わざわざクレームしに会社に来たのよ。じいさんが。

電話出なかったくらいでそこまでするか?って。うちはコールセンターじゃないのよ?

仕事でもメール全然返してくれない人とかいるけどさ、わざわざ会社に行ったりしなくね?しかもお前も出てねーじゃねーかって。

じいさん世代だと電話に出て当たり前なのかね?どんだけ暇なんだよ。くそじじい

2019-02-22

anond:20190221034413

起業詐欺と隣り合わせだし。

それはぶっちゃけ起業支援する方も情報商材屋としてね。

銀行詐欺師に利用されて、損害賠償請求されるの嫌でしょ?

しろ起業やってるのに、詐欺についての認識甘甘じゃん。自分詐欺師だと思われないような行動も心がけないとならんのに。

大家相談やな。許してくれることもある。

つーか、ランクル買えるぐらいの金あるのなら、もう一個一番最低クラス賃貸事務所にしていいという住宅の名のオフィスりりゃいいのに。

ましてや電話なんて安いもんだ。auのどこでも通話みたいなのいいぞ。ついでに、引くだけ引いといて常時転送というのもあるな。

2019-02-20

知り合いがクビらしい (4)

https://anond.hatelabo.jp/20190216125934 の続報。

とりあえず知人は何か所かの人材紹介サイト登録履歴書も何か所かに送付したようだが、人材紹介サイトからは速攻で×の通知がきているらしい。彼もちょっと滅入っているのか、こちらに「ひとつめ」「ふたつめ」と書いて転送してくる。めげるな。頑張れ。

2019-02-16

USB-CのPD対応ケーブルってlightningケーブルと同じぐらい高いよな

そのくせ給電仕様が複雑で給電する側とされる側の機器仕様確認して買わなきゃならんとなるとスゲーめんどくさい。

価格を落とすためにデータ転送速度削ってるケーブルとかまであってマジで地獄

2019-02-15

知り合いがクビらしい (2)

https://anond.hatelabo.jp/20190213153645 の続報。

知り合い、早速ハローワーク登録したのだが、それと併せていくつかの人材紹介サイト登録して、今と同じ業種の求人に応募し始めたらしい。

で……さっき「さっそくひとつめ。」と書かれた不採用通知転送メールが。ここでめげるな。長丁場になるかもしれないが、何とか居場所をゲットしてもらいたいと思うよ。

2019-02-12

エロ画像思ひ出

エロ動画ではない。エロ画像である。なにせこれは20年以上前の話だからだ。

当時、Windows ユーザの間でもモデムテレホーダイの併用でネット接続が普及しつつあった頃だった。俺は貧乏学生だったけど、常時接続された環境でのうのうとネットワークを使っていて(誤解を避けるために最初に書いておくが、俺は工学系だが情報系の専攻ではない)、Linux独立したサーバを組んで研究室に設置し、テレホーダイタイムに家の PC98 で hterm を走らせてターミナル接続し、emacsメールの読み書き、kermit で小さいファイルのやりとりをしているような、そんな感じの日々だった。テキストターミナルだけ、って、今の人には信じられないかもしれないけれど、メールネットニュースの読み書き、あとはサーバ管理を行う上では、これで何の不自由もなかった。まあそういう時代だったのだと思っていただきたい。

おそらくここを読んでいる方の多くは ネットニュースという言葉を聞いてもピンとこないと思う。誤解を恐れず簡単に言うと、オープンかつ分散的なネットワークで構成された5ちゃんねる、みたいなもの……かな。5ちゃんねるはオープンでも(ネットニュース程に)分散的でもない(いや内部では分散されているんだろうけどね)し、投稿匿名で行われるわけだけど、ネットニュースは個々のサーバ独立して運営されていて、上流のサーバとの間で NNTP によるバケツリレー方式ニュース記事ファイル転送される。ネット上を流れているニュースグループとその記事の数は膨大なもので、そこではほとんどの場合所属名前オープンにしたやりとりが行われていた。日本では fj.* ってのがあって…… void 氏とか lala 氏とか、何かまあ色々有名人物がいたわけだ。何か投稿する際にびくびくしながらやっていたのを今でも思い出す。

いや、まあ fj の話はよろしい。あくまでここではエロ画像の話だった。先のネットニュースニュースグループには comp.*, news.*, sci.* 等があったわけだけど、これらの枠組みに入らない、もしくは入れたくないような話題に関して収納する目的alt.* というのが作られていた。この alt.* はある意味無法状態に近くて、alt(言うまでもなくこれは alternative の略である)は実は "Anarchists, Lunatics and Terrorists" の略である、などと言われた位だった。そしてこの alt.* 内には alt.binaries.* というサブグループ形成され、そこに様々なバイナリデータが流されていた。ただし、ネットニューステキストしか流すことはできないので、バイナリデータuuencode(この頃まだ Base64 なんてなかったので)でテキスト文字列に変換され、分割されて投稿されていたわけだ。

で……長いな前置きが。要するに、alt.binaries.erotica.* というサブグループがあって、ここにエロ画像が大量に流れていたわけだ。勿論、大学企業ニュースサーバはこんなグループを購読したりはしないわけなのだが、あるときに噂が流れたのだ。**大学のこのサーバで、どうやら購読しているらしいぞ、と。アクセスのあからさまな制限がされておらず、見てみると……うわー、あらかた購読してるじゃん。まさに宝の山であった。ちょっと考えて、俺は自分サーバ上でスクリプトを書き始めた。

alt.binaries.erotica.* には、様々な性的嗜好に合わせた画像のサブグループがある。ガチムチホモの絡みなんてのはお呼びじゃないので、見目麗しそうな女性画像がありそうなグループをまず選び、そこの記事一定時間間隔(traffic を徒に増やすのはさすがに気がひけたので)で自動的採取、結合し、uudecode でバイナリに変換して HDD にストアする……そういうスクリプトを書いてみた。たまたまある目的で、データストア専用の HDDサーバに付けたところだったので、そこにバイナリを溜めるようにして、ちょっとわくわくしながら眠りについた。

翌日、早めに研究室に行き、他の学生がいないのを確認して HDD の中身を見ると……おー、溜まっとる溜まっとる。中には外れもあってスカトロやら妊婦やらエラいものも混じっているわけだが、さすがにこれは人力で弾くしかない(今ならそこも自動化するかもしれないが)。数日で HDD 一杯にファイルが蓄積されたのだった。さあ、めくるめくエロライフの始まりまり……と思ったのだが、そうはならなかった。結論から言うと、俺は1、2週間でそれをやめてしまったのだ。

まず俺は洋ピンマニアではなかった。そして、エロ画像ってのは飽きる。最初は、今風に言うと「これは俺の嫁」みたいなのを選んで、精選版画像アーカイブみたいなのを作ろうかとも思ったのだが、そういうところにそういう食指をそそられる画像ってまぁ流れてこないんだな。圧倒的に多いのは「ノイズ」。人力ノイズリダクションに嫌気がさしてしまったのだった。おまけに HDD は逼迫してくるし、結局あるところで意を決して、HDD を unmount してファイルシステムの再構築。すかーっと容量が空いたそのときが、実は一番快楽を感じた瞬間だったかもしれない。

まあ、あれだな。足るを知る、ってやつですよ。たくさんありゃいいってものでもない。それをちょっと学習したのだった。

2019-02-08

アレな動画をどうするか

割とよくある話だよね。俺の場合、使ってるノート PCHDD スロットと M.2 スロットが付いているので、M.2 の方にシステムを入れて、大容量の HDDHDD スロットに挿してる。で、その HDD の方に動画を……だな、その、溜めているわけだ。

でも、俺が急に命を落としたり、あるいは身動き取れない状態になったとき関係者が俺の PC 漁ってこれが出てきたら……と思うと、いつも不安になるわけだよ。どうするのが一番いいんだろう。

    • 断捨離とばかりに全消去(え、え、えぇぇぇぇ)

    ……どうしよう、と思いつつも、昨夜また俺の HDD の内容物は増えたのだった。

    2019-02-05

    anond:20190205094713

    それにしたって、社長印刷して経理に渡すんじゃなくてPDF転送して、経理印刷すればよくね?

    2019-01-30

    anond:20190130230459

    「パパは毎朝どこかから無限転送されてくる」とか言うのをネタだと思わない方がおかし

    2019-01-27

    anond:20190127115034

    性能にこだわると今どんだけ性能が出てるのか試験しないといけなくなるじゃん(負荷を与える強靭マシンネットワーク、莫大な転送量)

    から性能だけはこだわりたくない。

    2019-01-23

    anond:20190123125238

    バカじゃなきゃ

    内線>社用携帯転送設定してるよなぁ常考と思ってるのだと思うし

    鳴り続けるとうるせぇから取って不在を伝えた上で社用携帯転送するよう席の人間に伝えとけ

    2019-01-14

    カメラはやっぱり一眼とおもったのだが

    一眼レフ、重さやデカさは、写真とるためだから許容できるんだが、画像の共有のしにくさはいかんともしがたい。

    Wi-Fiついてても画像は重くて転送遅いし、スマホ用のサムネイル自動生成して、それだけ簡単に一括でスマホ転送したい。

    そして、欧米コンシュマー向け日本製品なんて、もうカメラくらいしかないし、欧米人なんだかんだで、一眼レフとか旅行にもってくの好きだから欧米販売コミュニティづくりがんばってほしい。

    アジアは加工された写真がすきだからスマホアプリに負ると思う。

    2019-01-08

    anond:20190108115242

    ちげーよ。人間は首の向きが同じでも目を動かして左右を見るだろうが。

    これをVR世界フィードバックできることが重要なのであって、頂点とかの転送量削れるやんみたいなのはただの副作用や。

    これでリアルポーカーVRとかを楽しめるようになるんやで(多分)

    2019-01-07

    音楽ファイルを古ノートからノートに移動中

    転送速度は3MB/s

    もうちょい速いほうがいいなあ

    新しい無線LANルーターとか買ったほうがいいかなあ


    いや、これひょっとして古ノートHDDの読み込み速度が律速なのか…?

    2019-01-03

    anond:20190103184241

    // WindowsProject7.cpp: アプリケーションエントリ ポイント定義します。
    //
    
    #include "stdafx.h"
    #include "WindowsProject7.h"
    
    #define MAX_LOADSTRING 100
    
    // グローバル変数:
    HINSTANCE hInst;                                // 現在インターフェイス
    WCHAR szTitle[MAX_LOADSTRING];                  // タイトル バーテキスト
    WCHAR szWindowClass[MAX_LOADSTRING];            // メイン ウィンドウ クラス名
    
    // このコード モジュールに含まれ関数宣言転送します:
    //ATOM                MyRegisterClass(HINSTANCE hInstance);
    BOOL                InitInstance(HINSTANCE, int);
    LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
    INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
    
    #include <list>
    
    class MyWindow;
    	
    std::list< MyWindow *> windows;
    
    class MyWindow
    {
    public:
    
    	HWND hWnd;
    
    
    
    	MyWindow()
    		:hWnd(NULL)
    	{
    		windows.push_back(this);
    	}
    
    	virtual ~MyWindow()
    	{
    		std::list< MyWindow *>::iterator it;
    		for (it = windows.begin(); it != windows.end(); it++)
    		{
    			if (*it == this)
    			{
    				windows.erase(it);
    				break;
    			}
    		}
    	}
    
    	static MyWindow * find(HWND key)
    	{
    		std::list< MyWindow *>::iterator it;
    		for (it = windows.begin(); it != windows.end(); it++)
    		{
    			MyWindow *target = *it;
    
    			if (target->hWnd == key)
    			{
    				return target;
    			}
    
    		}
    
    		return NULL;
    	}
    
    
    
    	//
    	//  関数: MyRegisterClass()
    	//
    	//  目的: ウィンドウ クラス登録します。
    	//
    	ATOM MyRegisterClass(HINSTANCE hInstance)
    	{
    		WNDCLASSEXW wcex;
    
    		wcex.cbSize = sizeof(WNDCLASSEX);
    
    		wcex.style = CS_HREDRAW | CS_VREDRAW;
    		wcex.lpfnWndProc = WndProc;
    		wcex.cbClsExtra = 0;
    		wcex.cbWndExtra = 0;
    		wcex.hInstance = hInstance;
    		wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT7));
    		wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    		wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    		wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT7);
    		wcex.lpszClassName = szWindowClass;
    		wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
    		return RegisterClassExW(&wcex);
    	}
    
    	//
    	//   関数: InitInstance(HINSTANCE, int)
    	//
    	//   目的: インスタンス ハンドルを保存して、メイン ウィンドウ作成します。
    	//
    	//   コメント:
    	//
    	//        この関数で、グローバル変数インスタンス ハンドルを保存し、
    	//        メイン プログラム ウィンドウ作成および表示します。
    	//
    
    	int blocks[100][100];
    
    	BOOL InitInstance()
    	{
    		hInst = hInstance; // グローバル変数インスタンス処理を格納します。
    
    		ATOM c = MyRegisterClass(hInstance);
    		x = 0;
    		y = 0;
    		boxType = 0;
    
    		hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
    			CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
    
    		for(int x = 0 ; x < 100 ; x++)
    		{
    			for (int y = 0; y < 100; y++)
    			{
    				blocks[y][x] = 0;
    			}
    		}
    
    		if (!hWnd)
    		{
    			return FALSE;
    		}
    
    		return TRUE;
    	}
    
    	BOOL ShowWindow()
    	{
    		BOOL ret;
    		ret = ::ShowWindow(hWnd, SW_SHOW);
    		::UpdateWindow(hWnd);
    
    		return ret;
    	}
    
    
    	HINSTANCE hInstance;
    	MSG msg;
    	BOOL run;
    	int x;
    	int y;
    	BOOL Main()
    	{
    
    		HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT7));
    		run = true;
    		int rc;
    		// メイン メッセージ ループ:
    		while (run)
    		{
    			DWORD obj = MsgWaitForMultipleObjectsEx(0, NULL,  100 , QS_PAINT| QS_ALLEVENTS,0);
    			if (obj <= WAIT_OBJECT_0)
    			{
    				while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    				{
    					if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    					{
    						TranslateMessage(&msg);
    						DispatchMessage(&msg);
    					}
    					if (msg.message == WM_QUIT) {
    						run = FALSE;
    					}
    					if (msg.message == WM_CLOSE) {
    						run = FALSE;
    					}
    
    				}
    			}
    			else if (obj == WAIT_TIMEOUT)
    			{
    				y++;
    				PAINTSTRUCT ps;
    				HDC hdc = BeginPaint(hWnd, &ps);
    				this->OnPaint(ps);
    				EndPaint(hWnd, &ps);
    				::UpdateWindow(hWnd);
    				RECT Rect2 = { 0,0,48*9,48 * 100 };
    				InvalidateRect(hWnd, &Rect2, TRUE);
    			}
    			else if (obj == WAIT_FAILED)
    			{
    				rc = GetLastError();
    			}
    			else {
    
    			}
    		}
    
    
    		return TRUE;
    
    	}
    
    	int boxType;
    
    	BOOL WriteBoxOLDBox()
    	{
    		int width = 24;
    
    		HDC hdc = GetDC(hWnd);
    		HBRUSH hBrush = CreateSolidBrush(RGB(48, 48, 48));
    		for (int y = 0; y < 30; y++)
    		{
    			for (int x = 0; x < 8; x++)
    			{
    				if (blocks[y][x] == 0)
    				{
    					continue;
    				}
    
    				RECT Rect = { 0,0,48,48 };
    				BOOL ret;
    
    				Rect.left = width * x + 1;
    				Rect.right = width * (x + 1) - 1;
    				Rect.top = width * y + 1;
    				Rect.bottom = width * (y + 1) - 1;
    
    				ret = FillRect(hdc, &Rect, hBrush);
    
    
    			}
    		}
    
    		DeleteObject(hBrush);
    
    		return FALSE;
    	}
    
    
    	BOOL WriteBox()
    	{
    		WriteBoxOLDBox();
    
    		switch (boxType)
    		{
    		case 0:
    			return WriteBoxI();
    		case 1:
    			return WriteBoxL();
    		case 2:
    			return WriteBoxZ();
    
    		}
    
    		return TRUE;
    	}
    
    	BOOL WriteBoxZ()
    	{
    		HDC hdc = GetDC(hWnd);
    		HBRUSH hBrush = CreateSolidBrush(RGB(48, 48, 246));
    
    		int width = 24;
    
    		RECT Rect = { 0,0,48,48 };
    		BOOL ret;
    
    		Rect.left = width * x + 1;
    		Rect.right = width * (x + 1) - 1;
    		Rect.top = width * y + 1;
    		Rect.bottom = width * (y + 1) - 1;
    
    		ret = FillRect(hdc, &Rect, hBrush);
    
    
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.left += width;
    		Rect.right += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    
    		DeleteObject(hBrush);
    
    		return TRUE;
    	}
    
    
    	BOOL WriteBoxL()
    	{
    		HDC hdc = GetDC(hWnd);
    		HBRUSH hBrush = CreateSolidBrush(RGB(48, 246 , 48));
    
    		int width = 24;
    
    		RECT Rect = { 0,0,48,48 };
    		BOOL ret;
    
    		Rect.left = width * x + 1;
    		Rect.right = width * (x + 1) -1 ;
    		Rect.top = width * y + 1;
    		Rect.bottom = width * (y + 1) -1;
    
    		ret = FillRect(hdc, &Rect, hBrush);
    
    
    		Rect.top    += width; 
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.left   += width;
    		Rect.right  += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		DeleteObject(hBrush);
    
    		return TRUE;
    	}
    
    	BOOL WriteBoxI()
    	{
    		HDC hdc = GetDC(hWnd);
    		HBRUSH hBrush = CreateSolidBrush(RGB( 246 , 48 , 48));
    
    		int width = 24;
    
    		RECT Rect = { 0,0,48,48 };
    		BOOL ret;
    
    		Rect.left = width * x + 1;
    		Rect.right = width * (x + 1) - 1;
    		Rect.top = width * y + 1;
    		Rect.bottom = width * (y + 1) - 1;
    
    		ret = FillRect(hdc, &Rect, hBrush);
    
    
    		//Rect.left   += width;
    		//Rect.right  += width;
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		DeleteObject(hBrush);
    
    		return TRUE;
    	}
    
    	BOOL SaveBoxI()
    	{
    		blocks[y  ][x] = 1;
    		blocks[y+1][x] = 1;
    		blocks[y+2][x] = 1;
    		blocks[y+3][x] = 1;
    		return TRUE;
    	}
    
    
    	BOOL OnPaint(PAINTSTRUCT &ps)
    	{
    		if (x > 8) {
    			x = 0;
    		}
    		if (x <0) {
    			x = 8;
    		}
    		if (y > 20) {
    			switch (boxType)
    			{
    			case 0:
    				SaveBoxI();
    				break;
    			case 1:
    				break;
    			case 2:
    				break;
    			}
    
    			y = 0;
    			boxType++;
    			if (boxType > 2)
    			{
    				boxType = 0;
    			}
    		}
    
    		this->WriteBox();
    
    		return TRUE;
    	}
    
    
    
    	BOOL OnKey(WPARAM wParam)
    	{
    		if (wParam == VK_LEFT)
    		{
    			x++;
    		}
    		if (wParam == VK_RIGHT)
    		{
    			x--;
    		}
    		return TRUE;
    	}
    
    
    };
    
    
    int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                         _In_opt_ HINSTANCE hPrevInstance,
                         _In_ LPWSTR    lpCmdLine,
                         _In_ int       nCmdShow)
    {
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
    
        // TODO: ここにコードを挿入してください。
    
        // グローバル文字列初期化しています。
        LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
        LoadStringW(hInstance, IDC_WINDOWSPROJECT7, szWindowClass, MAX_LOADSTRING);
        //MyRegisterClass(hInstance);
    
    
    
    	MyWindow win;
    
    
    
    	win.hInstance = hInstance;
    
    	// アプリケーション初期化を実行します:
    	if (!win.InitInstance())
    	{
    		return FALSE;
    	}
    
    	BOOL ret;
    
    	win.ShowWindow();
    
    	ret = win.Main();
    
    	if (ret)
    	{
    		return 0;
    	}else {
    		return (int)win.msg.wParam;
    	}
    
    
    
    }
    
    
    
    
    
    
    //
    //  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
    //
    //  目的:    メイン ウィンドウメッセージを処理します。
    //
    //  WM_COMMAND  - アプリケーション メニューの処理
    //  WM_PAINT    - メイン ウィンドウの描画
    //  WM_DESTROY  - 中止メッセージを表示して戻る
    //
    //
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
        case WM_COMMAND:
            {
                int wmId = LOWORD(wParam);
                // 選択されたメニューの解析:
                switch (wmId)
                {
                case IDM_ABOUT:
                    DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                    break;
                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;
                default:
                    return DefWindowProc(hWnd, message, wParam, lParam);
                }
            }
    		break;
    	case WM_KEYDOWN:
    		{
    			MyWindow *target = MyWindow::find(hWnd);
    			target->OnKey(wParam);
    		}
    	break;
        case WM_PAINT:
            {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hWnd, &ps);
    
    			MyWindow *target = MyWindow::find(hWnd);
    			target->OnPaint(ps);
    
    
                // TODO: HDC を使用する描画コードをここに追加してください...
                EndPaint(hWnd, &ps);
            }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    
    // バージョン情報ボックスメッセージ ハンドラーです。
    INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
        UNREFERENCED_PARAMETER(lParam);
        switch (message)
        {
        case WM_INITDIALOG:
            return (INT_PTR)TRUE;
    
        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
            {
                EndDialog(hDlg, LOWORD(wParam));
                return (INT_PTR)TRUE;
            }
            break;
        }
        return (INT_PTR)FALSE;
    }
    
    

    anond:20190103170543

    テトリス

    落ちてくるブロックの描き方

    わかりやす関数化してみた

    まずはブロックの種類は3種類

     

    WriteBoxLとWriteBoxIとWriteBoxZ

    の違いをよく見比べて

    自力で違いがわかれば

    プログラマーにはなれるとおもう

    とはいえ、コレを自力でわかるならもっと儲かる仕事あるとはおもうけどな

     

    	BOOL WriteBox()
    	{
    		switch (boxType)
    		{
    		case 0:
    			return WriteBoxI();
    		case 1:
    			return WriteBoxL();
    		case 2:
    			return WriteBoxZ();
    
    		}
    	}
    
    // WindowsProject7.cpp: アプリケーションエントリ ポイント定義します。
    //
    
    #include "stdafx.h"
    #include "WindowsProject7.h"
    
    #define MAX_LOADSTRING 100
    
    // グローバル変数:
    HINSTANCE hInst;                                // 現在インターフェイス
    WCHAR szTitle[MAX_LOADSTRING];                  // タイトル バーテキスト
    WCHAR szWindowClass[MAX_LOADSTRING];            // メイン ウィンドウ クラス名
    
    // このコード モジュールに含まれ関数宣言転送します:
    //ATOM                MyRegisterClass(HINSTANCE hInstance);
    BOOL                InitInstance(HINSTANCE, int);
    LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
    INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
    
    #include <list>
    
    class MyWindow;
    	
    std::list< MyWindow *> windows;
    
    class MyWindow
    {
    public:
    
    	HWND hWnd;
    
    
    
    	MyWindow()
    		:hWnd(NULL)
    	{
    		windows.push_back(this);
    	}
    
    	virtual ~MyWindow()
    	{
    		std::list< MyWindow *>::iterator it;
    		for (it = windows.begin(); it != windows.end(); it++)
    		{
    			if (*it == this)
    			{
    				windows.erase(it);
    				break;
    			}
    		}
    	}
    
    	static MyWindow * find(HWND key)
    	{
    		std::list< MyWindow *>::iterator it;
    		for (it = windows.begin(); it != windows.end(); it++)
    		{
    			MyWindow *target = *it;
    
    			if (target->hWnd == key)
    			{
    				return target;
    			}
    
    		}
    
    		return NULL;
    	}
    
    
    
    	//
    	//  関数: MyRegisterClass()
    	//
    	//  目的: ウィンドウ クラス登録します。
    	//
    	ATOM MyRegisterClass(HINSTANCE hInstance)
    	{
    		WNDCLASSEXW wcex;
    
    		wcex.cbSize = sizeof(WNDCLASSEX);
    
    		wcex.style = CS_HREDRAW | CS_VREDRAW;
    		wcex.lpfnWndProc = WndProc;
    		wcex.cbClsExtra = 0;
    		wcex.cbWndExtra = 0;
    		wcex.hInstance = hInstance;
    		wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT7));
    		wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    		wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    		wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT7);
    		wcex.lpszClassName = szWindowClass;
    		wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
    		return RegisterClassExW(&wcex);
    	}
    
    	//
    	//   関数: InitInstance(HINSTANCE, int)
    	//
    	//   目的: インスタンス ハンドルを保存して、メイン ウィンドウ作成します。
    	//
    	//   コメント:
    	//
    	//        この関数で、グローバル変数インスタンス ハンドルを保存し、
    	//        メイン プログラム ウィンドウ作成および表示します。
    	//
    	BOOL InitInstance()
    	{
    		hInst = hInstance; // グローバル変数インスタンス処理を格納します。
    
    		ATOM c = MyRegisterClass(hInstance);
    		x = 0;
    		y = 0;
    		boxType = 0;
    
    		hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
    			CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
    
    		if (!hWnd)
    		{
    			return FALSE;
    		}
    
    		return TRUE;
    	}
    
    	BOOL ShowWindow()
    	{
    		BOOL ret;
    		ret = ::ShowWindow(hWnd, SW_SHOW);
    		::UpdateWindow(hWnd);
    
    		return ret;
    	}
    
    
    	HINSTANCE hInstance;
    	MSG msg;
    	BOOL run;
    	int x;
    	int y;
    	BOOL Main()
    	{
    
    		HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT7));
    		run = true;
    		int rc;
    		// メイン メッセージ ループ:
    		while (run)
    		{
    			DWORD obj = MsgWaitForMultipleObjectsEx(0, NULL,  100 , QS_PAINT| QS_ALLEVENTS,0);
    			if (obj <= WAIT_OBJECT_0)
    			{
    				while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    				{
    					if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    					{
    						TranslateMessage(&msg);
    						DispatchMessage(&msg);
    					}
    					if (msg.message == WM_QUIT) {
    						run = FALSE;
    					}
    					if (msg.message == WM_CLOSE) {
    						run = FALSE;
    					}
    
    				}
    			}
    			else if (obj == WAIT_TIMEOUT)
    			{
    				y++;
    				PAINTSTRUCT ps;
    				HDC hdc = BeginPaint(hWnd, &ps);
    				this->OnPaint(ps);
    				EndPaint(hWnd, &ps);
    				::UpdateWindow(hWnd);
    				RECT Rect2 = { 0,0,48*9,48 * 8 };
    				InvalidateRect(hWnd, &Rect2, TRUE);
    			}
    			else if (obj == WAIT_FAILED)
    			{
    				rc = GetLastError();
    			}
    			else {
    
    			}
    		}
    
    
    		return TRUE;
    
    	}
    
    	int boxType;
    
    	BOOL WriteBox()
    	{
    		switch (boxType)
    		{
    		case 0:
    			return WriteBoxI();
    		case 1:
    			return WriteBoxL();
    		case 2:
    			return WriteBoxZ();
    
    		}
    	}
    
    	BOOL WriteBoxZ()
    	{
    		HDC hdc = GetDC(hWnd);
    		HBRUSH hBrush = CreateSolidBrush(RGB(48, 48, 246));
    
    		int width = 24;
    
    		RECT Rect = { 0,0,48,48 };
    		BOOL ret;
    
    		Rect.left = width * x + 1;
    		Rect.right = width * (x + 1) - 1;
    		Rect.top = width * y + 1;
    		Rect.bottom = width * (y + 1) - 1;
    
    		ret = FillRect(hdc, &Rect, hBrush);
    
    
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.left += width;
    		Rect.right += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    
    		DeleteObject(hBrush);
    
    		return TRUE;
    	}
    
    
    	BOOL WriteBoxL()
    	{
    		HDC hdc = GetDC(hWnd);
    		HBRUSH hBrush = CreateSolidBrush(RGB(48, 48, 246));
    
    		int width = 24;
    
    		RECT Rect = { 0,0,48,48 };
    		BOOL ret;
    
    		Rect.left = width * x + 1;
    		Rect.right = width * (x + 1) -1 ;
    		Rect.top = width * y + 1;
    		Rect.bottom = width * (y + 1) -1;
    
    		ret = FillRect(hdc, &Rect, hBrush);
    
    
    		Rect.top    += width; 
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.left   += width;
    		Rect.right  += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		DeleteObject(hBrush);
    
    		return TRUE;
    	}
    
    	BOOL WriteBoxI()
    	{
    		HDC hdc = GetDC(hWnd);
    		HBRUSH hBrush = CreateSolidBrush(RGB(48, 48, 246));
    
    		int width = 24;
    
    		RECT Rect = { 0,0,48,48 };
    		BOOL ret;
    
    		Rect.left = width * x + 1;
    		Rect.right = width * (x + 1) - 1;
    		Rect.top = width * y + 1;
    		Rect.bottom = width * (y + 1) - 1;
    
    		ret = FillRect(hdc, &Rect, hBrush);
    
    
    		//Rect.left   += width;
    		//Rect.right  += width;
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		Rect.top += width;
    		Rect.bottom += width;
    		ret = FillRect(hdc, &Rect, hBrush);
    
    		DeleteObject(hBrush);
    
    		return TRUE;
    	}
    
    
    	BOOL OnPaint(PAINTSTRUCT &ps)
    	{
    		if (x > 8) {
    			x = 0;
    		}
    		if (x <0) {
    			x = 8;
    		}
    		if (y > 8) {
    			y = 0;
    			boxType++;
    			if (boxType > 2)
    			{
    				boxType = 0;
    			}
    		}
    
    		this->WriteBox();
    
    		return TRUE;
    	}
    
    
    
    	BOOL OnKey()
    	{
    		x++;
    		return TRUE;
    	}
    
    
    };
    
    
    int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                         _In_opt_ HINSTANCE hPrevInstance,
                         _In_ LPWSTR    lpCmdLine,
                         _In_ int       nCmdShow)
    {
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
    
        // TODO: ここにコードを挿入してください。
    
        // グローバル文字列初期化しています。
        LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
        LoadStringW(hInstance, IDC_WINDOWSPROJECT7, szWindowClass, MAX_LOADSTRING);
        //MyRegisterClass(hInstance);
    
    
    
    	MyWindow win;
    
    
    
    	win.hInstance = hInstance;
    
    	// アプリケーション初期化を実行します:
    	if (!win.InitInstance())
    	{
    		return FALSE;
    	}
    
    	BOOL ret;
    
    	win.ShowWindow();
    
    	ret = win.Main();
    
    	if (ret)
    	{
    		return 0;
    	}else {
    		return (int)win.msg.wParam;
    	}
    
    
    
    }
    
    
    
    
    
    
    //
    //  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
    //
    //  目的:    メイン ウィンドウメッセージを処理します。
    //
    //  WM_COMMAND  - アプリケーション メニューの処理
    //  WM_PAINT    - メイン ウィンドウの描画
    //  WM_DESTROY  - 中止メッセージを表示して戻る
    //
    //
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
        case WM_COMMAND:
            {
                int wmId = LOWORD(wParam);
                // 選択されたメニューの解析:
                switch (wmId)
                {
                case IDM_ABOUT:
                    DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                    break;
                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;
                default:
                    return DefWindowProc(hWnd, message, wParam, lParam);
                }
            }
    		break;
    	case WM_KEYDOWN:
    		{
    			MyWindow *target = MyWindow::find(hWnd);
    			target->OnKey();
    		}
    	break;
        case WM_PAINT:
            {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hWnd, &ps);
    
    			MyWindow *target = MyWindow::find(hWnd);
    			target->OnPaint(ps);
    
    
                // TODO: HDC を使用する描画コードをここに追加してください...
                EndPaint(hWnd, &ps);
            }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    
    // バージョン情報ボックスメッセージ ハンドラーです。
    INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
        UNREFERENCED_PARAMETER(lParam);
        switch (message)
        {
        case WM_INITDIALOG:
            return (INT_PTR)TRUE;
    
        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
            {
                EndDialog(hDlg, LOWORD(wParam));
                return (INT_PTR)TRUE;
            }
            break;
        }
        return (INT_PTR)FALSE;
    }
    
    
    ログイン ユーザー登録
    ようこそ ゲスト さん