エンジニア in ハイパーカジュアル

こんにちは。技術部平山です。

今回は、ハイパーカジュアルというジャンルにおけるエンジニア、 というテーマで書きます。

勉強会でしゃべった動画がありますので、そちらを見て頂いても良いかと思います。 外に出すということで、普段よりも多少丁寧にしゃべっております。

前置き

平山が作った製品群

これらは2022年あたりから現在にかけて、平山が自分で企画、実装した製品です。 これらのうち、利益を出せた製品は2つあります。

黒字製品

の2つで、順に2800万、2100万ダウンロードです。加えて、いい線まで行ったものの、利益を出すに至らなかった製品が一つあります。

赤字だったTitanShoot

こちらは210万ダウンロードと、うまく行ったものの1/10程度しかありません。

ともかく合わせれば5000万ダウンロード程度で、 弊社のハイパーカジュアル製品の累計ダウンロード数は10億を超えていますから、 私の貢献は1/20くらいということになります。

このような経歴の人間が、ハイパーカジュアルという商売におけるエンジニアの仕事、 求められるスキル、生き甲斐、といったものについて書きます。

ハイパーカジュアルという商売

まず話の本筋に入る前に、ハイパーカジュアルというのがどのようなものかを簡単に説明します。 それがわからないと後の話がピンと来ないと思うからです。 もし5年前の私にこの商売の話をしたとしたら、全く理解できなかったでしょう。

まずハイパーカジュアルというのは、私の定義では 「全人類向け暇潰しスマホアプリ」 です。 「ハイパーカジュアルゲーム」と「ゲーム」をつける人もいますが、 私はつけない方が実態を良く表していると思います。 ミニゲームの一種と言えなくもないですが、ミニゲームという言葉が生ぬるく感じられる ほどに仕様が小さく、例えば私はタイトル画面すら作りません。 switchのゲームとハイパーカジュアルは、 2時間の映画とtiktokくらいに違い、同じ名前で呼ぶのがはばかられるほどです。

そのように仕様が小さいため、開発費は安く、 必然的に客単価も低くなります。 基本的には客単価50円以下で、そのかわりに数で勝負します。

私がこのジャンルで大ヒットと考えるのは1億ダウンロードを超えてからで、 3000万でようやくヒット、1000万なら残念な製品といったところでしょう。 私が利益を出せた2製品は、「ヒットと呼ぶには苦しい程度の成功」と考えていただけると良いかと思います。

収益構造

商売というものは、安く仕入れて、高く売ると利益が出ます。 ハイパーカジュアルでも同じです。

ただし、「仕入れ値」が開発費でなく広告費で決まる点、 「売り値」が広告報酬である所に特色があります。 通常のゲーム開発では、開発の人件費が「仕入れ値」の大半を占めますが、 ハイパーカジュアルはあまりに仕様が小さいため、 開発費はあまり問題になりません。 プロトタイプなら最小1/4人月程度(1人で1週間)、 作り込みまでやっても最大で3人2ヶ月程度(6人月)です。 ほとんどはプロトタイプの段階で止まるため、 製品の大半は1/4から1/2人月程度の開発費しかかかりません。 おそらく 人類が望み得る最小の開発費であろう と思います。 AIが作るようにならない限り、これ以上はないでしょう。

さて開発費はこのように安いのですが、そのかわりに、広告費をかけます。 我々の製品の広告をいろいろな所に出し、 それでインストールしてもらい、 このアプリに他社の広告を出し、 その広告報酬が、払った広告費よりも大きくなれば、 その差額が利益となります。

例えば、平均50円の広告費でインストールしてもらい、 平均55円の広告報酬が発生すれば、5円の利益となります。 仕入れ値が50円、売価が55円、ということに相当するわけですね。 そして、1000万人にインストールしてもらえれば、 5000万円の利益が出る計算になります。 5億円の広告費を投入して5.5億円の売上を得る、 というなかなかにスリリングな商売です。

この構造を念頭に置いて以下を読まれると理解が早いかと思います。

エンジニアの仕事

エンジニアの仕事のサイクルは、プロトタイプと作り込みに分けられます。

プロトタイプは、異常に速い人で3日、速めの人で1週間、平山のように遅めの人なら2週間 程度で開発します。企画、実装、広告動画作成、ストアでの公開、 までをその期間で行います。 ほとんどのプロトタイプは望みがないとしてそれ以上の開発はしませんから、 ほぼほぼ ずっとゲームジャム 状態になるわけです。

そして運良くプロトタイプが良い成績を出して有望となれば、 作り込みが始まります。そこまで行くのは1/10でも良い方です。 そして、作り込んでも黒字にまで持っていけないケースは多々あり、その場合も開発が中断されます。

平山は今まで4年くらいハイパーカジュアル商売に参加していますが、 自分でプロトタイプを作るようになったのは2年くらい前です。 なのに15製品しか作っていないわけで、 1つ2週間と考えると7.5ヶ月ですから計算が合いません。 残りの期間は他の人が考えたゲームを実装したり、 成績が良かったものの作り込みをしており、 その間は無限ゲームジャム状態から逃れられるわけです。 もし有望な製品が一つも出なければ、最初に貼った製品リストは50個くらいになっていたでしょう。 あるいはその前に私が外し続けることに耐えられなくなって、 ハイパーカジュアルを去っていたかもしれません。

プロトタイプ

プロトタイプは、せいぜい2週間で作ります。平山は遅い部類なので それを標準にしてお話をするのは心苦しいのですが、 実際問題、年単位で続けるなら2週間ペースを基本にするのが妥当と 個人的には考えています。身体が持ちませんし、 1週間で作れるものしか作らなくなれば、 似たようなものばかり作ることになりやすく、それではヒットが出なくなります。 ただし、最初のうちはまず1週間で作れるようになることを目指すべきです。 1週で作れるようになった後で2週に落とす、というくらいがいいかなと思いますし、 たまには1週で作って速度感を取り戻した方がいいと思います。

さて人数ですが、平山は主に一人で考えて一人で実装するやり方を取ります。 もちろん企画と実装の2人で分業することもありますし、 企画、アート、実装の3人で分業することもあります。 共用のノートに皆が雑なアイディアをメモしているので、 そこから着想を得て作る、といった方法もあります。 何が得意かによってやり方は変えるべきですし、 その時の気分によって変えたって良いでしょう。 「たまたま話した企画がビビッとくるアイディアを出してきた」というような巡り合わせも重要です。

実際、Swing Blade(Android, iOS) という製品は企画、アートと組んで3人で作りました。 コードは私が書き、レベル(ステージ)の実装は企画が、モデルやライティング、マテリアルはアートが担当しています。

スケジュール

2週間というのは組織のルールでもなんでもなく、平山が自分で課しているだけです。 しかし、それを破ると際限なくダラけてしまうので、 よほどのことがない限りはこの範囲で作るようにし、 超えてしまった時には次の製品を1週で作るよう努力していました。

2週間というのは10営業日で、だいたい以下のような分配になります。

  • 企画はほぼ0日
    • 大抵は作っている最中に「次はこれだな」というイメージができているので、考えて手が止まることはない。
    • 作る物が決まっていないと土日も頭から離れず考えてしまうので、大抵は月曜には決まっている。
  • 遊びの核と技術開発に6-7日
    • 後述するように毎回何かしらの技術的な挑戦を入れているので、ここに時間を食う。
    • 敢えて技術的な困難がないものを作ったり、企画をもらっていたりするとここが短縮される。
    • ただし、早く終わればその分だらけるので、あまり早くならない。
  • 遊びを広げる検証とデータの用意に1-2日
    • 実装した仕掛けの範囲内でどんな遊びが作れるかを検証するために、8-10レベル程度を作る。
    • 「おもちゃ」的なゲームではレベルの数は少なくなりがち。
  • ストア公開は大抵2周目の木曜日
    • 金曜に出したのでは審査通過が金曜の深夜に間に合わないことがあるため、木曜に出すことを基本としている。
    • うちではストアにアプリを出す権限は全員が持っているので、誰に許可を取ることもなく勝手に出す。
  • 広告動画は2周目の金曜日の夕方
    • 後述するようにここはあまり凝らないので、1時間かせいぜい2時間あればできる。

技術と遊びの検証にかなりの時間を割いており、ただ出すだけならばここはもっと短縮できますが、 平山はしません。平山はアイディアで勝負するタイプではなく、 ここを削ると似たようなものしか作れなくなるからです。

なお、平山も最初から2週間で作れたわけではありません。 最初は「作らなくていいもの」の塩梅がわからず、 1ヶ月以上かかることもありました。 次第にやらなくていいことがわかってきますし、 周りが速いとプレッシャーを感じるので速くなります。

何を省くか

普通に考えて、ゲームが2週間で作れるはずがありません。 ゲームジャムなら多少粗くても仕方ないですが、 これはあくまでも製品です。価値があると思うもの、 面白いと思うものを出さなければ意味は薄れますし、失礼です。

そこで、大胆に仕様を削ることになります。 例えば以下のような作業はプロトタイプ段階ではまずやりません。

  • タイトル画面
  • 設定画面
  • 多言語対応
    • そもそも言語を入れないことが理想。せいぜい"Start" "Get"程度
    • スマホゲームは音を出さないで遊ぶ人の比率がかなり高く効率が悪いのです
    • ただし気分が乗ると入れることもあります。楽しいので。
  • アイコン等のUIの新規開発
    • 過去作から流用するか、そもそも一切なしで済むようにします
  • 3Dモデルの新規開発
    • アセットストアから持ってくるか、過去作から流用するか、コードで生成するかで済ませます
    • つまり「独自デザインのキャラクター」のようなものなしで成立することを基本とします
  • 長く遊ばせるためにやること
    • ゲーム内でお金を溜めてアイテムを買う、とかはやらない
    • レベルをたくさん用意する必要もない(平山は「半日作業で用意できる程度」が多い)

みもふたもない言い方をすれば、 動画に映らない物は一切実装しなくともかまいません

ただし、プロトタイプ段階でもどれくらい遊ばれたかは計測でき、 その情報も後々のために大事ですから、 まともに遊べないようなものを作れば、自分の首を締めることになります。 そして何よりも、遊べないようなもの、明らかにつまらないものを出すことを 私は自分に許したくありません。 私はダメだったプロトタイプも含めてほぼ全部の自分の製品を気に入っており、 スマホには入れっぱなしになっていてたまに遊びます。

広告動画

作ったアプリの魅力が伝わるように動画を作り、 それを広告としていろんな所に流し、 どれくらいインストールされたかで「このプロトタイプが有望か」を判断します。 インストールされた数が最重要指標ですから、それだけを考えるのであれば、 動画こそが全てです。

しかし、だからといって凝った動画を作るのは良くないというのが、 うちでの共通認識になっています。 「プレイ動画を撮ってカットしてつなぐ、以上のことはしない」 というのがおおよその了解です。

なぜなら、それ以上のことをすれば、それはアプリが持つ魅力でなく、 編集で作られた魅力になってしまうからです。 アプリの力を測ることができなくなります。 また、そんなことに時間をかけていれば、プロトタイプを作るコストが増して 数を打てなくなりますから、手間は最小で良いのです。 大抵は最終日金曜の夕方に1時間くらい使って作って、マーケティング担当に渡しています。

アプリに魅力があれば、単に録画してそのまま流しても良い結果は出ます。 事実、上に挙げた「利益が出た2製品」のうちの1つであるMannequin Downhillでは、 最初に出した動画はカットすらしていませんでした。 20秒プレイ動画を録画して、ただ出しただけです。 Draw Saberの方はもうちょっと欲をかいて、 2-3秒のカットを10個くらいつないでいますので若干手間がかかっていますが、 それでも再生速度をいじったりはしていません。ただつないだだけです。 後々広告動画のプロが編集テクニックを駆使してよりウケる広告を作ってくれるだろうと考えれば、 その分だけ伸び代があるということになります。

なお、これに使うソフトはAfterEffectsが多数派ですが、 平山は「タダで使える奴でいいや」と思ってShotcutを使っています。 カットしてつなぐだけなら十分すぎます。

プロトタイプの成績とは

プロトタイプを作っては捨て、作っては捨て、というのが我々の日常なわけですが、 あるプロトタイプが「良いのか」「悪いのか」を決める手続きを説明していませんでした。

広告動画の所で少しだけ書きましたが、つまるところは「たくさんインストールされたら良いアプリ」です。

広告動画を作り、少額の広告費を払ってどこかで広告を流してもらいます。 例えば週末に1万円払うとしましょう。その結果、月曜までに100人がインストールしてくれたのであれば、 「一人あたり100円の広告費でインストールしてもらえる」ということがわかります。 インストールしてくれたのが200人であれば、一人あたりは50円です。 つまり「一人当たりの払う広告費」は安いほど良く、 この安さをもって「アプリがどれくらい有望か」を測っています。 この「一人当たりの払う広告費」をCPI(cost per install)と称します。

もちろん、アプリを実際にどれくらい遊んでもらえるか、 というのは別の問題でして、一瞬で飽きるゲームだと他社の広告を出す余地すらなく、 いくらCPIが安い良いアプリであっても、黒字にはできません。 しかし、この段階ではロクに作り込んでいませんから、 そのアプリがどれくらい長く遊ばれるポテンシャルを持つかは未知数です。 なので「とりあえずCPI60円切れば後でどうにかできるだろう」 といった厳しめのラインで決めます。

甘めに設定すると、 「CPI100円で開発継続してみたが60円しか回収できない完成度で頭打ちになった」 みたいなことが起こり、継続して開発する分コストがかかりますからダメージが大きくなります。 この段階の基準はたぶん厳しめの方が良い気がします。

なお、プレイ時間の平均、起動回数の平均、レベル(ステージ)クリア数の平均、 最終面をクリアした率、といったものは、プロトタイプの段階でも計測できます(adjustと、 カスタムイベントの組み合わせで可能です)。 合否に反映させるかどうかは組織ごとの考えがあると思いますが、 「この遊びが面白いのかつまらないのか」 がわかることはゲーム開発者にとっては重要です。 プロトタイプの不合格がえんえん続くと合格することしか考えなくなりがちですが、 合格しても黒字にならなければ意味はありませんから、 「合格した後に黒字になるくらいに面白くできる余地があるのか」 は重要です。 平山が作るものはプレイ時間12-13分程度が多く、たまに20-23分といったものも出ます。 TitanShootはCPIで合格したもののプレイ時間が6分程度しかなく、 非常に悪い予感がしていました。この段階でのプレイ時間が長い方が、 おそらく後の戦いは楽になります。

作り込む

プロトタイプがたくさんインストールされてCPIが低いとわかり、 「これは望みがあるぞ」となったら、今度は作り込みを開始します。

この段階ではアプリの作りは粗いしボリュームもないはずで、 大抵の場合この段階では払った広告費を超える広告報酬は得られません。 そもそも広告が出るようにもしていないでしょう (私はこの段階では広告SDKを入れることすらしません)。

というわけで、まず広告が出るようにし、 どれくらいの広告報酬が得られるかを測定します。 ハイパーカジュアルアプリでの広告報酬の主力は 「インタースティシャル(Interstitial)広告」と呼ばれるもので、 レベル(ステージ)の合間にゲームが中断して全画面の広告が流れるものです。 画面の下に常時出ているバナーはあまり高い報酬になりませんし、 「広告を見るとアイテムがもらえます」的なリワード広告は そもそも「配るアイテム」を実装する手間がかかりますから、 最初の主力はインタースティシャルにならざるを得ません。

仮に一人あたり広告費を50円払っている(=CPIが50円)のであれば、 広告報酬を一人あたり50円頂かねば商売になりません。 そして広告報酬を増やすには、 広告一回あたりの報酬額を増やすか、広告表示回数を増やす必要があり、 前者は我々にはどうにもなりませんから、後者がメインです。

そして、プレイ時間が増えれば広告を出すチャンスは増えますから、 つまるところ ゲームを面白くしてプレイ時間を伸ばせば良い という極めて明快な結論に達します。

面白いゲームを作れば利益になる という非常にシンプルかつ 幸せな結論で、我々は面白いゲームを作りたいわけですから これ以上に幸せなことはないと言えますね。

広告報酬とダウンロード数

さて、今までは話を単純化するために、 まずCPIがいくらかに固定されて、広告報酬がそれを超えれば利益が出る、 というような話にしてきました(上の動画でもそれ以上の話はしていません)。 しかし実は話はこう単純ではありません。

まずCPI、つまり一人あたり広告費は固定ではありません。 より高い広告費を払えば、より「人が来てくれやすい場所」 に広告を出すことができます。 例えば、電車に広告を出すよりもテレビに出す方が広告の効果は大きそうですよね? でもお金もかかりそうですよね? だいたいそういう話です。

ですので、ゲームの質を高めた結果広告報酬が上がれば、 それだけ高い広告費を払うことができるようになります。 高い広告費を払うほどお客さんはたくさん来ますので、 利幅は減りますが、利益の総額を大きくすることができます。

例えば、50円の広告費を払って60円の広告報酬を得れば一人当たり10円の利益になります。 しかしこれで来るお客さんが100万人にすぎなければ、利益は1000万円に留まります。 ここで、もし55円の広告費を払うことでお客さんが500万人に増えるのであれば、 利幅は5円になりますが、利益は2500万円に拡大します。 広告報酬が増えれば、利幅を保ったままでより高い広告費を払え、 それによってお客さんの数を増やすことができるのです。

面白いゲームを作れば利益が得られる というのはこういうことで、 この「作り込み」もまた死活的に重要な段階となります。

開発費を回収しなければ総合的には黒字になりませんから、 仮にCPIを超える広告報酬を達成しても、 十分な人数に遊んでもらえなければダメなのです。

例えば開発費が400万円なのであれば、一人当たり4円の利益なら100万ダウンロード 必要です。実際には「当たらなかった多数のプロトタイプ」の開発費も 払わねばなりませんから、必要な利益はその10倍はゆうに超えます。 「1000万ダウンロードでは残念」と前に書いたのはそういうことです。

「アプデ地獄」

さて、この追加開発はどんな様相を呈するのでしょうか。

平山の場合、週に2,3回のアプリ更新(アップデート=アプデ)を行うのが普通です。 週5、6回になることも珍しくありません。 なぜなら、この段階でもたつくと致命的な結果になりかねないからです。 労働時間的にはプロトタイプより辛い状況にもなります。

なぜそんなに急ぐのかと言えば、 一度アプデをかけた段階で、他社に「それが有望なアプリであること」 が知られてしまうからです。 望みがないアプリを更新する人はいません。 同じアイディアでより高い完成度のものを作られてしまえば、我々は敗北します。

また、単純にこの段階でモタモタしているとコストがかかりすぎます。 プロトタイプ段階では大した人件費はかけませんが、 黒字を目指してアプデをするとなれば、 組織ぐるみで人員を投入することも考えられます。 そうすれば同じ期間でもかかる人件費は数倍になりますから急がざるをえません。 そして、ここまで来ても結局黒字にできないという可能性はそこそこあり、 「結局ダメなのであればもっと早くあきらめておくべきだった」 ということにもなりかねません。

さらに、「黒字にするのずいぶん手こずってるね。捨てて別のもっと有望な プロトタイプ作った方がいいんじゃないの?」とか言われちゃうと せっかくがんばって良いCPIを出しても台無しになります。 自分のゲームを世に出す機会を逃したくはありませんよね?

以上のことから、プロトタイプを作っては捨て作っては捨て、 している時よりも、黒字を目指してアプデしまくる時期の方が 精神的には辛かったりします。これは人によると思いますが。

アプデの内容

ではアプデでは何をするのでしょうか。

まずやることは、プロトタイプの「粗さ」を取り除くことです。 プロトタイプはなにせ2週間で作っているので、基本的な所から 粗いことがほとんどです。

そもそも操作方法を間違っている、カメラワークが悪い、 操作性が悪い、難度が適切でない、 不必要にCPU負荷が大きい、無駄に容量が多い、 変なところで処理落ちする、クラッシュがある、 等々、「粗さ」と言えるような欠点は多数あります。 まずこれを除くことが重要です。

広告動画を見てくれた人は何かしらの期待を抱いて インストールしています。 プレイ時間が長くならないのは、基本的にはその期待を裏切っているからです。 例えば動画を見てお客さんが想像した操作方法と異なる、 というようなことがあると遊ぶのをやめてしまうきっかけになります。

さて、このような初歩的な問題をあらかた解消できたら、 今度は遊びの拡張と物量の追加に話を移します。 遊び方の幅が狭いとすぐ飽きますから、幅を広げることは真っ先に考えます。 何を足せば効果的に遊びを広げられるか、は重要な問いです。

例えばMannequin Downhillは基本的に「道を進んでいくレースゲーム的な何か」 であり、コースが増えれば遊びが広がります。 しかし似たようなコースをいくら増やしても遊びは広がりません。 なので、「カーブの度合い」「凹凸の度合い」「障害物」「崖」のような コースに配置できる要素をまずある程度実装し、 それによってどう遊びが広がるか、どう組み合わせれば面白いか、 といったことを検証、実験します。

また、レースゲームでは自キャラを選べるようにすることで ゲームを広げるのは常套手段です。 「コース数×自機種類数」と掛け算で遊びが広がる可能性があります。 加速が遅いが最高速が速い、加速が良いが最高速が遅い、カーブを曲がりやすい、 といった特徴付けがよくなされますが、Mannequin Downhillでは 「羽がついていて揚力で浮く」「バイク」「人の形ですらないただのボール」 といったあからさまに違った自機をいくつか用意しました。 競争要素はなくタイムすら出ないので、有利不利よりも操作感が違うことが重要だからです。

なお、ゲーム内に「お金」を導入してアイテムを買えるようにしたり、 パラメータを強化できるようにしたりする拡張も 有力な選択肢になりますが、ゲームのシンプルさを損ないやすく、 画面に表示されるものもゴチャゴチャしやすいので、 下手にやるとマイナスになることが多い印象です。 そもそもそれが効果を発揮できるような遊びでなければ、 せいぜい「見た目が変わる」程度のことにしかならず、 なかなか効果は見込めなかったりします。 手間ばかりかかって効果が薄く、 遊びの核に関係ない要素を足すのは面倒くさいし嫌なので、 平山はその手の改造はあんまりやらないことが多いです。 しかしそういった強化が意味を持つような遊びを最初から設計した上であれば、 非常に大きな改善になる可能性を秘めています。 TitanShootではいくらかこれを試みましたが、黒字にできなかったので 中途半端な状態で終わってしまいました。

ABテスト

上記のような改善が、実際にプレイ時間を改善できるかどうかは案外わかりません。 むしろ、かえって改悪されるケースの方が多いとすら言えます。

「こうすればもっと面白いに違いない」は、基本的に開発者の思い込みであり、 それがお客さんにとって賛同できるものかどうかは試してみるまでわからないものです。 ですので、平山はほとんど全ての改造に関してABテストを行っています。

まず、Androidでは同時に2バージョンを配布できますので、 いきなり新バージョンを100%配布せず、新旧バージョンを同時に配布します。 そうすれば、データを取って統計学的手法を用いることで、 どちらのバージョンの成績が良いかを推測することができます (ABテストについては過去にブログにしています)。

また、ある一つのバージョンの中でも、例えばレベルの並びや内容を何セットか用意し、 お客さんごとにランダムに割り当てて、どれの成績が良いか判断する、ということもあります。

AB Testの例

これはABテストの状況を簡単に見られるように作ったツールの画面です。 ツールといってもスプレッドシートにつけたスクリプト(GAS)でして、 大したことはしていません。 2つのバージョンの比較、レベルの並びや、カメラワーク、効果音の設定、 などに関するABテストの成績がグラフの形で表示されています。 bigQueryを触るのが億劫な人のために作ったのですが、 結局は自分でも使っています。

黒字になるか

こんな感じでアプデを繰り返し、黒字に持っていきます。 DrawSaberは20回、MannequinDownhillは60回ほどアプデしています。

黒字になった後に黒字幅を拡大するために行ったアプデもありますから、 黒字まで持っていくためにそれだけの回数が必要とも限りません。 しかし、黒字に持っていくのは大抵大変です。 最初の目標は何よりも「意味のある人数が来るだけの広告報酬額を達成すること」ですが、 これが結構遠いからです。

CPIを安くすれば、つまり安い場所にしか広告を出さなければ、 「お客さん一人当たり」を黒字にすること自体は簡単です。 広告報酬がかなり低くても「お客さん一人あたり」は黒字になります。 しかし、前述のように、CPIが安ければお客さんの人数が減ります。 一日100人しかインストールされなければ、せいぜい利益は500円程度でしょうから、 いかに開発費が数百万と安くても、回収は不可能です。

CPIが高くてプロトタイプで終わる製品の開発費まで含めて回収しようと考えれば、 一日に数万インストールされる所まで持っていかねばならず、 そもそも「黒字にする」という段階でもハードルはかなり高いのです。

プロトタイプは「どうせ当たらない」と考えて気楽に好きなものを作っていても いいのですが、作り込みを始めてしまうとそういう気楽さがなくなってしまいます。

実際、TitanShootは40回のアプデを行いましたが、黒字に至りませんでした。 「爆死」と呼んで差し支えないでしょう。

しかし、黒字を達成し、さらに自分の人件費分以上に稼ぎ続ける状況にまで至れば、 そういったプレッシャーは全て消えます。 そこからは、自分の理想のゲーム目指して、やりたかったことをやれば良いのです。 飽きるまで。当てたんだから、それくらい良いでしょう?

ただし、前述のABテストの手続きを経なければ実装をアプリに入れることはできませんし (私がそういうルールを自分に課しているだけですが)、 元々の仕様が小さいので完成度を上げたり拡張したりするにも限界が来やすいという事情があります。 さらに、改善が進めば進むほど広告報酬は上げにくくなります。 10面を20面に増やす、というならば10面をクリアするくらいの人はたくさんいるので効果も上がりますが、 100面を110面に増やす、となると、100面までクリアした人しか恩恵を受けません。 総合的な成績が上がりにくいのも道理です。いずれABテストの手続きを突破できなくなり、 費用対効果的にも正当化できなくなる日が来ます。単純に飽きることもあるでしょう。

そうなれば、またプロトタイプを作る日々に戻ることとなります。しかしこれは幸せな終わり方です。

エンジニアに要求されるスキル

さて、ここまでエンジニアの仕事の実際を紹介してきました。 違和感を覚える方もいらっしゃるでしょう。「それはエンジニアの仕事なのか?」という。

ここまでに出てきた仕事のスキルを雑に分類すると以下のようになります。

  • 実装力
    • 仕様を実装して形にする力
    • 容量や処理速度に配慮したアプリを作る力
    • 高度なアルゴリズムを用いて他ができない事を実現したり、品質を達成したりする力
  • 発想力
    • 面白い企画を考えつく力
    • ハイパーカジュアルの市場で受け入れられる条件を把握する力
  • 視覚デザイン力
    • 数秒で伝わるための配色、画面配置をする力
    • 重要な情報を取捨選択して、それをアートやライティングを通して画面に反映させる力
  • 動画作成力
    • 数秒で目を引き、遊びの核の面白さをダイレクトに伝える動画を作る能力
  • 遊びを改善する力
    • 客が何を面白いと思っているか、何を不満に思っているかを推測して対処する能力
    • 遊びを広げるゲームデザインの知識や能力
    • レベルなどのデータとして実際に遊びを具現化する能力(レベルデザイン)
  • データ分析力
    • 数千から数十万人の行動データをどう使えば検証したい仮説が検証できるかを考える能力
    • 統計学的な知識を使って、より小さいデータで、より確実な判断をする能力
    • SQLその他のツールを使ってデータ処理を実行する能力
  • 耐久力
    • 何度プロトタイプを外しても動じずに淡々と作り続けられる能力
    • 改善を何十回重ねてでもゲームを改善していく粘り強さ

通常の「エンジニア」という言葉から想像されるのは一番上だけでしょう。 データ分析もエンジニアの範疇かもしれませんが、 Unityで実際ゲームを作るエンジニアと同一人物であることは、普通は要求されないと思います。

しかし平山は結局これを全部自分でやる状況になっていますし、 自分でやることの利点というのは確実にあります。 これらの能力を全て高レベルで持っているなどとは口が裂けても言えませんし、 明らかに苦手な能力もあるわけですが、それでも一人でやることで 余計な人件費をかけず、コミュニケーションコストも不要になり、 自分が作りたいものを作れます。

そして、「自分はエンジニア」と狭く規定してしまうよりも、自分の幅を広げることができます。 自分が想像もしていなかった適性を持っていることが明らかになったりもします。 「唯一エンジニアだけが、これらを全部一人でやることができる」ということは重要です。 コードが全く書けない企画やアーティストが実装含めて一人でやることは極めて困難であり、 これはエンジニアの特権と言えます。 プログラマがゲームを作っていた時代がまた戻ってきたのです。

ですが、なんぼなんでも、これらの能力を一人に期待するのは無茶がすぎます。 ですから、自分に苦手なことはやらずに済ませたり、他人に任せたりすることが 普通は合理的です。

例えば、技術力で勝負したくなければ、難しい技術を避けて作ることはできます。 Unityで物理主体で作れば、コードを書く必要性は大きく減じます。 データ分析をやりたくなければ、複雑なABテストは避け、バージョン間比較だけを行った上で、 統計学的検定は他人に投げたりツール化してもらう手があります。 アートはアセットストアでまかなったりアーティストに頼むのがむしろ普通でしょう。 平山の場合、発想力には甚だ自信がないので、発想で勝負しない戦い方を心がけています。 これは後で詳しく述べます。

また、段階で分業することもできます。 プロトタイプを作るのは得意でも、その後の改善には自信がなく、そもそも興味もない、 というケースもあるでしょう。そういう場合にはプロトタイプができた段階で 他人に丸投げし、自分はまたプロトタイプ開発に戻る、といったことはよくあります。 実際2年以上前の平山は、他の人のプロトタイプを受け取って黒字まで持っていく仕事をメインにしていました。

個性はさまざまですから、自分に合った所で貢献する方法を模索していくべきです。 そうでないと続きませんし、成果も出しにくいかと思います。 ぶっちゃけ「自分は技術にしか興味がないからゲームの内容は知らん」 という人であっても、その技術が頭抜けているならば、 うまく使う方法はあるはずです。極めて高い技術を要求し、他社に真似できないようなものを作ることができ、 それがヒットするのであれば、相当有利に戦えます。 「他社には作れない」と確信していれば、類似製品を恐れる必要はないのです。

ところで、分業が持つ問題点も少しだけ指摘しておきます。

例えばプロトタイプと改善を分離すると、プロトタイプをやる人はずっとプロトタイプ開発になり、 極めて高頻度にアイディアを要求された結果、疲弊してしまうということが起こりえます。 また、企画と実装を分離した結果、実装担当者が「これは自分のゲームだ」 と感じられず、イマイチ気分が乗らずに品質が落ちたり、仕事が嫌になってきたりする危険があります。 広告動画を開発者以外にお願いした場合には、 頼んだ相手に遊びの核を理解してもらうのに手間をかけないといけなかったり、 うまく伝わらなかったりするリスクがあります。 平山はそういったリスクを大きく評価するタイプなので、 「多少苦手でも自分でやっちゃえ」となってしまうわけですが、 そういった欠点をうまく抑えつつ分業することで専門性を活かして効率と品質を上げられるのであれば、 その方が良いのです。実際、私は広告動画があまり得意でないようで、 他の人にも作ってもらった時には結構な確率でそちらの方が良い成績を出します。

エンジニアの生き甲斐

ここまでで、「ハイパーカジュアルのエンジニアの仕事は、エンジニアっぽくない」 という話になってきた感じがします。

さて平山はエンジニアなのでしょうか?

正直もはやわかりません。何を作るかを自分で考えるようになった段階で、 エンジニアの領分を踏み超えているような気もします。 平山は「それぞれの専門性を活かすために組織がある」 と考えていますので、本来は分業を指向する人間なのです。 過去に書いた本 でも「仕様を考えるのは企画だし絵を書くのはアーティスト」 というようなことをはっきりと書いていて、今も基本は変わっていません。 なのに結局は一人で物を作っているという今の状況がなんとも不思議に思えます。

しかし、それはそれとして、自分はやはりエンジニアなのではないか、 という気もするのです。最後にそのお話をいたします。

技術駆動ハイパーカジュアル開発

平山は、基本プロトタイプを技術から着想しています。 「今回はこの技術をやりたい」「じゃあそれで作れる遊びはこれだな」 といった話の流れです。 「この題材がウケそう」「こんなゲームが面白そうだ」 といったものは二の次で、最初に「今回はこの技術を使う」 と決めてしまいます。 そして、二週間しかないのに、その半分を平気で技術に割いてしまいます。

しかも恐ろしいことに、その技術が「世の中にすでに実装されてパッケージ化されている」 としても自作してしまうのです。 「それもうライブラリあるよ」と言われるのが怖いので、調べることすらしません。 いきなり自分で作り始めます。 理由はいろいろありますが、究極的には「自分がやりたいから」でしかありません。 「自分でやったことを最大に利用して結果に結びつける工夫」はしますし、 少なくともDrawSaberやMannequinDownhillではそれが良かったのでしょうけれども、 まっとうな態度とは言えないでしょう。 つまり、私は結局のところ「自分で実装したい」というエンジニアのエゴで 動いています。その意味で、私はエンジニアなのではないかと思うわけです。

そこで、少しこれまでに作ったもののことを振り返ってみようかと思います。

Unreal Sumou → Draw Saber

Unreal Sumou

これは、2021年の末に、会社の「業務外開発イベント」で作った相撲ゲームです (こちらの記事が詳しいです)。 企画者のトモぞヴP は相撲で普通に格ゲーを作る気でいたようですが、 私はその意図をガン無視して、 既成のアニメーション(AnimationClip)を入れず、 完全物理制御で気持ち悪い相撲もどきを作り始めました。

立たせることすらできず腰に見えないフックをひっかけて吊っている状態で、 技術的には稚拙もいい所なのですが、それが醸し出す気持ち悪さのおかげか ちょっとバズったりしていい気になっていた気がします。

これが、DrawSaberの種になります。

「せっかくだしハイパーカジュアルにしよう」と思った時に、 相撲という題材はありえません。世界に売るのに相撲はないでしょう。 そして、スマホという特性を考えると、パンチとキックボタンがあるような操作設計は 合いません。過去に作り込み作業をやったParkMasterのことを思い出しました。 線を引いて攻撃を出せば直感的です。 しかし、パンチとキックを使い分けるのは面倒でしょう。 そこで武器を持たせました。武器を持っていれば武器を動かすのが自然です。

運良く、売れました。

しかし、やはり腰はフックで吊っている状態であり、「物理で立つ」 という目標は果たせませんでした。この思いが次につながります。

Mannequin Downhill そして

その少し前、別の人が作ったあるプロトタイプがいい成績を出し、 私が作り込みを試みたことがありました。

Ragdoll Master

バイクに乗った人形が、坂を下って、そのうちコケると人形が バイクから吹き飛ばされて飛び、落下した所で「飛距離XXXメートル」 のような表示が出ます。

次のスタート時にはエンジン等々を強化でき、それで飛距離が伸びていく、 という作りです。

私の力が及ばず黒字にはできなかったのですが、思ったことがありました。 「これ、バイクじゃなくていいのでは」。

いろいろ試された結果、広告動画は吹き飛んだ後のシーンしか映っておらず、 バイクに乗っている時間はほぼゼロでした。 人体が吹き飛ぶのがウケているのであれば、バイクはいらないことが想像できます。 だったら最初から走って坂を駆け降りて、足がもつれてコケた方が面白いのでは?

しかし、当時二足で走って坂を下らせる方法が想像もできず、 放置になっていました。そもそもDrawSaberの時点ですら、 「立つこと」もできていなかったのです。走らせるなど到底無理でした。

普通なら走るアニメーションを事前に作っておいて再生するのでしょうが、 私は「物理でやらないと面白くならない」という信念に強く支配されており、 物理でやれないうちは作らないと固く誓っていました。

そしてDrawSaberが出た後プロトタイプを作っていた時、これのことをふと思い出したのです。 「今なら走らせられるのではないか」と。

そうしてチャレンジした結果、どうにか腰を吊らずに立たせることには成功したのですが、 約束の2週間はあと3日しかない状態でした。 そこでやむをえず妥協したのです。 タイヤをつけようと。

Mannequin Downhill

それがMannequin Downhillでした。そして売れました。

さらにこれを発展させ、当時できなかった「二足走行」を実現した プロトタイプを作れたのが数ヶ月前です。これも作り込み段階へと進めることができました。

技術駆動という選択肢

これらは成功例ですが、成果が出なかった製品もほぼ全てが技術駆動です。 「メッシュ切断を自作したい!」というエゴで宝石をカットするアプリを作ったり、 「魚眼レンズやりたい!当たったらVRにしたい!」という無謀な思いで作ってみたりと、 どれを思い返しても技術が先行しています。 その魚眼レンズに挑戦したTitanShootも、作り込みをやれる所までは行きました。 そこで爆死したわけではありますが、技術駆動で作るようなマネをしても、 低いCPIは達成できるということです。

技術駆動で作ると、必然的に真似されにくい製品ができます。 「すでにその処理を高品質で行うライブラリが配布されている」 というケースでは別ですが、そうでなければ類似製品を出すのは大変ですし、 仮に同等以上の機能を持つライブラリが配布されていたとしても、 特に物理分野では使いこなすのが難しいケースが多いように思います。 であれば、多少は真似されにくくなるでしょう。

また、アイディアが出なくても気にせず作れるという利点もあります。 「どんな題材で、どんなゲーム性にしたらウケるか」 を考えるのは大変です。普段からたくさんの物を見て、 四六時中何が面白いか考えているような人間でなくては、 そうそう当たるものは作れません。しかし技術駆動なら話は楽です。 まず「できたらウケそうな技術」を選定します。 例えば「人が二足で走って坂を下ってコケたらおもろいだろ」 となれば、二足で走れればいいわけです。 あとは、「二足で走ってコケる」を実現すれば、 もうそれで遊びの核はできています。そこを動画に映せば面白いはずです。 そういう仮説なわけですから。 カメラを横にするか見下ろしにするか後ろにするか、 などは、「走ってコケるの見るのがおもろい」という遊びの核や、 スマホで許容される操作方法から逆算で絞り込まれるので、 そう迷うことはありません。

「魚眼」の場合も、魚眼が活きるとすれば視線を動かした時です。 とりわけ見上げたり見下ろしたりする上下の動きでは強く画面が歪み、「他にない」感じが出ます。 また、魚眼にすることで通常よりも広範囲を1画面に収められますから、 視野が広いことが意味を持つ遊びが良いだろう、ということはすぐわかります。 となると「自分は移動せず、視線を動かしながら360度から襲ってくるデカい巨人が近づいてくるのを撃つ」 というのは、まあそれが最も面白いかはわかりませんが、妥当な解として 必然的に導かれると思います。アイディアが出なくてもいいわけです。

さらに、技術駆動であれば、何かプロトタイプを作る度に 何かしらの技術が進歩しているので、合格しなくてもあんまり気にならない、 という精神的な利点もあります。「今回はこれができるようになったなあ」 という満足感があるので、外しても気にならないのです。 当てるために必死にならないのはダメなのでは、 と思う方もいらっしゃるかと思いますが、 必死になったくらいで当たるものでもありません。 精神を楽に保ち、作るペースを落とさずに、 あまり似たものばかり作らないようにする、という方が重要だろうと私は思います。 技術駆動であれば似たものばかりは作りませんし、それが楽しければ 作るペースも維持されますから、それで良いのではないでしょうか。

平山もいずれ成果が出なくなる日が来るでしょうが、 その時にはハイパーカジュアルを引退して「エンジニア」に戻るだけのことです。 ヒットを出すか出さないか、といった商売はそう長く続けられるものではなく、 今こんな仕事をしていられること自体が奇跡、あるいは冗談のようなものでしょう。 夢オチだったとしても驚きません。

さいごに

というわけで、今回はまずハイパーカジュアルという商売の形と、 その中でエンジニアがやる仕事、スキル、生き甲斐、 といった話を書いてみました。

平山は過去の20年あまり、ゲームの内容にはあまり関与せず、 技術者として専門で仕事を全うすることを良しとしてきたわけですが、 人生何があるかわかりませんね。

「5000万ダウンロードマン」という謎の称号を得ることができ、 「この意味わからない祭はいつ終わるのかな?夢オチじゃね?」 と思いながら日々を送っています。ハイパーカジュアルのような新しい商売に 参加すると、そういう意味不明な祭に巻き込まれることがあって大変人生を楽しめますので、 おすすめです。大変ですが。

平山が考えるに、ハイパーカジュアルは「ゲーム開発の極北」です。 これ以上に狂った開発形態は人間が作る限りないのではないかと思います。 「豪華」と「手軽」の戦いは「手軽」が有利、とTrade Offにも書かれていますし、 このようなものの市場はおそらくなくならないでしょう。 すでに死ぬほどレッドオーシャン化しているとはいえ、 まだまだ世界のスマホ人口は増えますし、 経済の重心が徐々に新興国に移る過程でチャンスも出てきます。 むしろレッドオーシャン化することで似たようなものを作っても勝ちにくくなるのであれば、 「他社が作らないものを市場に叩きつけて勝つ」 という楽しい挑戦をやるのにうってつけの環境になりますから、 なおさら楽しめると思います。 自らを「エンジニア」と自認し、日々技術を磨いていらっしゃる方であれば、 そのような戦い方は望む所ではないでしょうか。

そういうわけで、エンジニアの居場所としてハイパーカジュアルを選ぶことは、 そう悪くはないのではないか、と平山は考えています。 ...まあそう言う平山が3ヶ月後に耐えられなくなっているかもしれませんが。

ちなみに、元々これはCEDECに応募したネタでした。 落ちたので供養しようと思い、社内勉強会で録画し、動画を見るのが面倒な方のために 文章化もしてみた次第です。

【JS体操】JavaScript で頭の体操をしよう!〜第一問 44文字 解説編〜

こんにちは!カヤック面白プロデュース事業部のおばらです。
普段は受託案件、特にインタラクティブな WebGL や Canvas2D を駆使する案件のデザイン&実装を担当しています。

先日出題したJS体操 第一問目、挑戦してくださったみなさまありがとうございました!
早速ですが最短文字数の回答は 44文字 でした!

export default x=>x-(x%=.2)+.2-(.04-x*x)**.5

みごと44文字を達成した方は、

  • halwhite さん
  • koyama41 さん
  • sugyan さん
  • tkihira さん
  • たつけん さん

の5名!(※ Unicode コードポイント順)
おめでとうございます!!

最短文字数を狙った正統派の回答以外にも、裏技的な面白アプローチがたくさんありました笑
このアプローチは面白い、ぜひ紹介したい!という回答がいくつかあったので、解説記事は2回に分けてお送りしようと思います。

  • 第一回(本記事)最短文字数44文字の解説、実務での応用例の紹介
  • 第二回(次回)その他の面白いアプローチの回答、裏技的な回答の紹介

なお、締切は一応過ぎましたがまだ回答の送信は(いつでも)可能です。 締切は過ぎたけど「44文字よりも短くできた!」という方はぜひ送信お願いします。

もし「まだ挑戦していない!」という方はこの記事をそっと閉じ、ぜひ『JS体操』してみてください!

hubspot.kayac.com

『JS体操』とは?

『JS体操』とはカヤックが主催する JavaScript のコードゴルフ大会です。
今回がその第一回目でしたが、もともとは社内の勉強会として始めた施策です。
その詳細は以下のブログ記事を御覧ください!

techblog.kayac.com

techblog.kayac.com

問題のおさらい

黒い実線が赤い点線のガイドとぴったり重なるように f.js を編集し、且つコードの文字数をできるだけ短くしてください!

波形を整形する問題ですね!
波形の整形はアニメーションに使うイージング関数を自作 or カスタマイズしたりする際に必要なテクニックです。
画像処理でトーンカーブをいじりたいときなどに使うケースもあるでしょう。

それにしても変な形のグラフです。
どんな式(コード)になるのでしょうか?
考えていきましょう!

考え方

複雑な形状のグラフを、複数のシンプルなグラフを合成したもの、と考えてみましょう。
グラフを図形的に捉えると、以下のように2つの図形の「足し算」や「引き算」で表せそうです。

足したり引いたりしているグラフは比較的シンプルなので、ちょっと考えやすくなりましたね!
というわけで「足し算」作戦と「引き算」作戦のそれぞれを試してみましょう。
さぁ、Let's JS体操!

作戦① 足し算

上の図を数学的に考え、2つのグラフ(関数)の和と捉え直してみると、こうです!

 f(x) g(x) の2つの式が求まれば良さそうです。

f(x) を考える

f(x)床関数(floor function)と呼ばれるグラフ(関数)です。
f(x) を JavaScript で書くと、例えばこうです。

const f = x => {
  return Math.floor(5 * x) / 5;
};

数学的には以下も同じです。しかし後述の理由により JavaScript 的には同じとは言い切れません。

const f = x => {
  return Math.floor(x / 0.2) * 0.2;
};

今回はコードゴルフする前提なのでより文字数が短い前者でいきましょう。
つまりこちら。

const f = x => {
  return Math.floor(5 * x) / 5;
};

さらに今のうちになるべく短くしておきましょう。
まず return を省略します。

const f = x => Math.floor(5 * x) / 5;

Math.floor(n) 0 \leqq n であれば Math.trunc(n) で代替できますね。
そして Math.trunc(n)は(整数部分に)副作用の無いビット演算で代替できます。

副作用がなければどんなビット演算でもよいです。

例えば n >>> 0n >> 0n << 0~~nn | 0 など。

今回は一番文字数の少ない n | 0 を使いましょう。

const f = x => (5 * x | 0) / 5;

※ ビット演算により数値の内部的な型が64ビットの浮動小数点から符号付き32ビット整数となり、その結果小数部分が切り捨てられます
※ ビット演算の副作用を利用する際、値が符号付き32ビット整数で表現できる範囲を超えてオーバーフローしないように注意しましょう
※ 演算子の優先順位に注意しましょう

無事、床関数が描けました!

g(x) を考える

g(x) はちょっと厄介そうですね。
同じ形状が5つ並ぶ周期的な関数です。

まずはその1つを p(x) と置きます。

このカーブは、ぱっとみ、円の右下1/4っぽいです。
円の式から作っていきましょう。
※ 以降、数学的に厳密には正しくない説明があるかもしれませんがご容赦ください

原点を中心とする半径 0.2 の円の式は以下です。

 x^{2} + y^{2} = 0.2^{2}

y軸の正の方向に 0.2 ずらします。

 x^{2} + (y - 0.2)^{2} = 0.2^{2}

最終的に  y = ??? の形にしたいため、x^{2}を右辺に移動します。

 (y - 0.2)^{2} = 0.2^{2} - x^{2}

次に2乗を外します。

 y - 0.2 = \pm\sqrt{0.2^{2} - x^{2}}

今回必要な範囲では y-0.2 \leqq 0 より

 y - 0.2 = -\sqrt{0.2^{2} - x^{2}}

最後に  -0.2 を右辺に移動して

 y = 0.2 - \sqrt{0.2^{2} - x^{2}}

できた!

※ もし数式が崩れている!という方は、以下を試してみてください!

JavaScript で書き直すと、

const p = x => {
  return 0.2 - Math.sqrt(
    Math.pow(0.2, 2) - Math.pow(x, 2)
  );
};

 \sqrt{n}n0.5 乗なので Math.sqrt(n)Math.pow(n, 0.5) と書き直せますね。
そして Math.pow(n, m) は ES2016 から使えるべき乗演算子(exponentiation operator)を用いて n**m と書き直せます!

const p = x => 0.2 - (0.2**2 - x**2)**0.5;

 p(x) が描けました!

あとはこれを5回繰り返せばよいです。

繰り返すには、以下のようなのこぎり波(sawtooth wave)を入力しましょう。

のこぎり波を  q(x) と置きます。

JavaScript に直すと、

const q = x => x % 0.2;

ですね!

p(x)q(x) を合成すると、

無事  p(q(x)) つまり  g(x) ができました!

const p = x => 0.2 - (0.2**2 - x**2)**0.5;
const q = x => x % .2;
const g = x => p(q(x));

f(x) に g(x) を足す

準備は整いました。

 f(x) g(x) を足し合わせましょう。

export default x => {
  const f = x => (5 * x | 0) / 5;
  const p = x => 0.2 - (0.2**2 - x**2)**0.5;
  const q = x => x % .2;
  const g = x => p(q(x));

  return f(x) + g(x);
};

さて、後はコードゴルフをするだけ、と思いきや

大変です!

 x = 0.6 x = 1.0 で上に飛び出ちゃいました。

浮動小数点の誤差っぽいです。

const q = x => x % .2;

const q = x => 5 * x % 1 / 5;

と書き直してみましょう。

ちょっと文字数が長くなってしまいましたが直りました!

これと同様、 f(x)

const f = x => (5 * x  | 0) / 5;

ではなく

const f = x => (x / 0.2 | 0) / 5;

と書くと、今度は  x = 0.6 で下に飛び出てしまうので注意しましょう。

浮動小数点の誤差、厄介ですね。

ちなみに、この浮動小数点の誤差でグラフがおかしくなる現象は、例えば graph.js の 194 行目をMath.fround()を使い以下のように修正するなどで回避できるかな、、、と思いましたが(改善はしたものの完全には)できませんでした。
そこで、浮動小数点の誤差も回避してください!というルールにしちゃいました笑

さて、浮動小数点の誤差の問題を回避したコードをさらに短くしていきましょう。
Let's CODE GOLF!

export default x => {
  const f = x => (5 * x | 0) / 5;
  const p = x => 0.2 - (0.2**2 - x**2)**0.5;
  const q = x => 5 * x % 1 / 5;
  const g = x => p(q(x));

  return f(x) + g(x);
};

 p(x) q(x) を展開します。

export default x => {
  const f = x => (5 * x | 0) / 5;
  const g = x => 0.2 - (0.2**2 - (5 * x % 1 / 5)**2)**0.5;

  return f(x) + g(x);
};

 f(x) g(x) も展開します。

export default x => {
  return (5 * x | 0) / 5 + 0.2 - (0.2**2 - (5 * x % 1 / 5)**2)**0.5;
};

return や不要な空白などを省略します。

export default x=>(5*x|0)/5+.2-(.04-(5*x%1/5)**2)**.5

ちょっと整理します。

export default x=>(5*x|0+1)/5-(.04-(5*x%1/5)**2)**.5

もうちょっと詰めれるような気はしますが、これまでの作戦①では44文字の達成は難しそうです。

一旦諦めて、作戦②を試しましょう。
コードゴルフでは複数のアプローチを並行で試すのが大事です!

作戦② 引き算

さて、ちょっと頭を切り替えて、今度は引き算で考えてみましょう。

グラフにするとこうなります。

でもこの  g(x)、答えがわかってるので描けますが、頭ではちょっと想像しづらいですね。

 g(x) は図形的に考えることにしましょう。

f(x) を考える

f(x) は超簡単。シンプルな一次関数です。

const f = x => x;

g(x) を考える

前述の通り  g(x) のグラフはこうです。

が、頭で想像しづらいので図形的に考えます。

作戦①のときと同様、1周期分を考えましょう。

すると、これも2つの図形の引き算で表せそうですね!

青い三角形は  f(x)= x(の一部)

そして赤い引く方の図形は、、、見覚えがありますね!

そう、作戦①の  p(x) = 0.2 - \sqrt{0.2^{2} - x^{2}} です。

グラフで考えなおすと

またまた作戦①に登場したのこぎり波  q(x) を使って周期的にすると、 g(x) ができました!

さぁ、いよいよ JavaScript で書いていきましょう!

f(x) から g(x) を引く

export default x => {
  const f = x => x;
  const p = x => 0.2 - (0.2**2 - x**2)**0.5;
  const q = x => 5 * x % 1 / 5;
  const g = x => q(x) - p(q(x));

  return f(x) - g(x);
};

 f(x) を展開します。

export default x => {
  const p = x => 0.2 - (0.2**2 - x**2)**0.5;
  const q = x => 5 * x % 1 / 5;
  const g = x => q(x) - p(q(x));

  return x - g(x);
};

 g(x) も展開します。

export default x => {
  const p = x => 0.2 - (0.2**2 - x**2)**0.5;
  const q = x => 5 * x % 1 / 5;

  return x - q(x) + p(q(x));
};

 p(x) q(x) も展開します。

export default x => {
  return x - (5 * x % 1 / 5) + 0.2 - (0.2**2 - (5 * x % 1 / 5)**2)**0.5;
};

return や不要な空白を省略します。

export default x=>x-(5*x%1/5)+.2-(.04-(5*x%1/5)**2)**.5

(5*x%1/5) が2回登場するので、(x=5*x%1/5) で一回で済ませます。

export default x=>x-(x=5*x%1/5)+.2-(.04-x**2)**.5

(今回に限っては) 5*x%1/5x%.2 と書き直しても浮動小数点の誤差の影響もなさそうです。

export default x=>x-(x=x%.2)+.2-(.04-x**2)**.5

x=x%.2x%=.2 とします。

export default x=>x-(x%=.2)+.2-(.04-x**2)**.5

そして最後!

x**2x*x とすると地味に1文字減ります。

export default x=>x-(x%=.2)+.2-(.04-x*x)**.5

やった!!44文字!!!!!

ようやくたどり着きました。
長かった、、、おつかれさまでした!

応用例

今回のようなグラフを作るテクニックは実際の業務でどのように応用できるのでしょうか? 例として、アニメーションのイージング関数に使うサンプルをご紹介します!

位置のアニメーションに使ってみる

const f = x=>x-(x%=.2)+.2-(.04-x*x)**.5

  • 横軸:時間
  • 縦軸:縦方向の位置

とすると例えばこんな動き。

https://codepen.io/tsmallfield/pen/zYXbGKx

See the Pen JS体操 No. 1 解説用サンプル① by Tohl SMALLFIELD (@tsmallfield) on CodePen.

回転のアニメーションに使ってみる

const f = x=>x-(x%=.2)+.2-(.04-x*x)**.5

  • 横軸:時間
  • 縦軸:回転角度

とすると例えばこんな動き。時計の秒針みたい笑

https://codepen.io/tsmallfield/pen/NWmJqzJ

See the Pen JS体操 No. 1 解説用サンプル② by Tohl SMALLFIELD (@tsmallfield) on CodePen.

スケールのアニメーションに使ってみる

const f = x=>x-(x%=.2)+.2-(.04-x*x)**.5

  • 横軸:時間
  • 縦軸:スケール

とすると例えばこんな動き。だんだん迫ってきます笑

https://codepen.io/tsmallfield/pen/KKYEpxO

See the Pen Untitled by Tohl SMALLFIELD (@tsmallfield) on CodePen.

このようにいろいろ応用が可能です。

コマアニメーションにも使える

イージング関数以外に、 実はコマを1コマずつ進めていく処理にもまた別のグラフ  g(x) を使っています。
本記事でも登場した、(周期的な)床関数ですね!

const g = x=>(5*x%1)*16|0;


※ グラフの縦軸の範囲を 0.0〜16.0 にしています

アニメーションのループにも使える

さらにアニメーションを無限にループさせる処理にも別のグラフ  h(x) を使っています。
本記事でも登場した、のこぎり波ですね!

const h = x=>x%1;


※ のこぎり刃であることがわかりやすいよう、グラフの横軸の範囲を 0.0〜5.0 にしています


アニメーションは何らかのパラメータの時間変化なので、グラフで考えると楽ですね!
イージング関数については以前こんな記事も書いています。ぜひご覧ください!

techblog.kayac.com

まとめ

最後まで読んでいただきありがとうございます!
44文字の解説、いかがでしたでしょうか?

実は問題を作った私も当初自力では44文字に到達できませんでした。
Math.sqrt(n)n**.5 と書けることになぜか気づかなかったり。 \sqrt{n} n^{0.5} であることは知っているのになぜ…
言われてみればそうだよな、と思うことでもなかなか思いつかない、気づかないことってありますよね。ほんと不思議です。笑

  • 一つのアプローチにこだわらず、複数のロジックを比較したり
  • 数学脳と JavaScript 脳をうまく切り替えてみたり
  • エディタの中だけで考えず、紙と鉛筆を使ってみたり
  • デスクだけでなくお風呂や電車の中などいろいろな場所で考えてみたり

など、コードゴルフの奥義がまだまだたくさんありそうですね。

そしてなにより、みなさんの回答がとても勉強になりました。
「こんなアプローチがあったのか!」とか「こんな裏技できたか!」などたくさんの刺激・気づきが得られました。複数の回答を送っていただいた方も多く、感激です。挑戦していただいた皆さま、ありがとうございました。

実際の業務でコードゴルフ自体が求められることはまず無いのですが、コードゴルフに必要な知識と技術は実際の業務でもとても重要です。言語仕様の理解を深めたり、プログラミング的な思考力を高めるのにもってこい。そしてなにより、頭の体操として面白いですよね。
今後も、実際の業務に応用できそうな、解いていて面白い問題を出していきますので ぜひ、日々の鍛錬・息抜きに『JS体操』はじめてみませんか!

次回は、みなさまに投稿していただいた裏技的アプローチのコードをいくつかご紹介します。
想定していなかった面白回答がたくさんですよ!

お楽しみに!!

お知らせ

次回以降の「解説ブログ」や「JS体操の問題」のお知らせが気になる方はこちらにご登録ください。
公開次第、ご連絡します!

hubspot.kayac.com

実は先日、Perl のコードゴルフコンテストもカヤック主催で開催されました。
こちらも面白いのでぜひご覧ください! techblog.kayac.com

そして、カヤックではコードゴルフが大好きな新卒&中途エンジニアも募集しています!

www.kayac.com www.kayac.com