0
0

[JavaScript]なんでこんなにガチャガチャしたくなるのか。。。

Posted at

基本ゲームは無課金勢

ゲームは好きで課金さえすれば無双できるのはわかっているけど、
いつサ終するかわからないデータにお金かけるくらいなら、
ドッグラン行ったり・水槽買ったり・餌用コオロギ買ったりするほうが、
有意義と思ってしまうくらいゲームへの優先度が年々下がってきてる。。。

やっぱりガチャ回したくね?

無課金でもアホほどガチャ回したい(ガチャガチャきゅ~と・ふぃぎゅ@メイト
そんな気持ちにお答えして、自分の好きな条件でいっぱい回せるガチャを作ってみた。
過去にガチャの話してたので、ソレの簡単実装って位置付け

そう言えば、この業界入って死ぬほど残業させられていた頃、
すもももももも(アニメ)で知った MOSAIC.WAV の
【キミは何テラバイト?】と【Magical Hacker ☆ くるくるリスク】を、
頭おかしくなるほど聞いてたお陰で立派なプログラマーになれました!
みんなも頭おかしくなるくらいレッツ、リッスン!!

とりあえず簡易ガチャ(キャラ設定がアホほど長いやつ)

gacha1.js
// キャラ一覧
/**
 * ■排出率
 * SSR:なんかとっても凄いキャラ1 → 0.30%(リスト内の出現数 / リストの総数)
 * SSR:なんかとっても凄いキャラ2 → 0.30%
 * SSR:なんかとっても凄いキャラ3 → 0.30%
 * SSR:なんかとっても凄いキャラ4 → 0.30%
 * SSR:なんかとっても凄いキャラ5 → 0.30%
 * SR:とっても凄いキャラ1        → 1.52%
 * SR:とっても凄いキャラ2        → 1.52%
 * SR:とっても凄いキャラ3        → 1.52%
 * SR:とっても凄いキャラ4        → 1.52%
 * SR:とっても凄いキャラ5        → 1.52%
 * R:凄いキャラ1                 → 3.03%
 * R:凄いキャラ2                 → 3.03%
 * R:凄いキャラ3                 → 3.03%
 * R:凄いキャラ4                 → 3.03%
 * R:凄いキャラ5                 → 3.03%
 * R:凄いキャラ6                 → 3.03%
 * R:凄いキャラ7                 → 3.03%
 * R:凄いキャラ8                 → 3.03%
 * R:凄いキャラ9                 → 3.03%
 * R:凄いキャラ10                → 3.03%
 * N:ただのキャラ1               → 6.06%
 * N:ただのキャラ2               → 6.06%
 * N:ただのキャラ3               → 6.06%
 * N:ただのキャラ4               → 6.06%
 * N:ただのキャラ5               → 6.06%
 * N:ただのキャラ6               → 6.06%
 * N:ただのキャラ7               → 6.06%
 * N:ただのキャラ8               → 6.06%
 * N:ただのキャラ9               → 6.06%
 * N:ただのキャラ10              → 6.06%
 */
const charList = ["SSR:なんかとっても凄いキャラ1"
  , "SSR:なんかとっても凄いキャラ2"
  , "SSR:なんかとっても凄いキャラ3"
  , "SSR:なんかとっても凄いキャラ4"
  , "SSR:なんかとっても凄いキャラ5"
  , "SR:とっても凄いキャラ1"
  , "SR:とっても凄いキャラ1"
  , "SR:とっても凄いキャラ1"
  , "SR:とっても凄いキャラ1"
  , "SR:とっても凄いキャラ1"
  , "SR:とっても凄いキャラ2"
  , "SR:とっても凄いキャラ2"
  , "SR:とっても凄いキャラ2"
  , "SR:とっても凄いキャラ2"
  , "SR:とっても凄いキャラ2"
  , "SR:とっても凄いキャラ3"
  , "SR:とっても凄いキャラ3"
  , "SR:とっても凄いキャラ3"
  , "SR:とっても凄いキャラ3"
  , "SR:とっても凄いキャラ3"
  , "SR:とっても凄いキャラ4"
  , "SR:とっても凄いキャラ4"
  , "SR:とっても凄いキャラ4"
  , "SR:とっても凄いキャラ4"
  , "SR:とっても凄いキャラ4"
  , "SR:とっても凄いキャラ5"
  , "SR:とっても凄いキャラ5"
  , "SR:とっても凄いキャラ5"
  , "SR:とっても凄いキャラ5"
  , "SR:とっても凄いキャラ5"
  , "R:凄いキャラ1"
  , "R:凄いキャラ1"
  , "R:凄いキャラ1"
  , "R:凄いキャラ1"
  , "R:凄いキャラ1"
  , "R:凄いキャラ1"
  , "R:凄いキャラ1"
  , "R:凄いキャラ1"
  , "R:凄いキャラ1"
  , "R:凄いキャラ1"
  , "R:凄いキャラ2"
  , "R:凄いキャラ2"
  , "R:凄いキャラ2"
  , "R:凄いキャラ2"
  , "R:凄いキャラ2"
  , "R:凄いキャラ2"
  , "R:凄いキャラ2"
  , "R:凄いキャラ2"
  , "R:凄いキャラ2"
  , "R:凄いキャラ2"
  , "R:凄いキャラ3"
  , "R:凄いキャラ3"
  , "R:凄いキャラ3"
  , "R:凄いキャラ3"
  , "R:凄いキャラ3"
  , "R:凄いキャラ3"
  , "R:凄いキャラ3"
  , "R:凄いキャラ3"
  , "R:凄いキャラ3"
  , "R:凄いキャラ3"
  , "R:凄いキャラ4"
  , "R:凄いキャラ4"
  , "R:凄いキャラ4"
  , "R:凄いキャラ4"
  , "R:凄いキャラ4"
  , "R:凄いキャラ4"
  , "R:凄いキャラ4"
  , "R:凄いキャラ4"
  , "R:凄いキャラ4"
  , "R:凄いキャラ4"
  , "R:凄いキャラ5"
  , "R:凄いキャラ5"
  , "R:凄いキャラ5"
  , "R:凄いキャラ5"
  , "R:凄いキャラ5"
  , "R:凄いキャラ5"
  , "R:凄いキャラ5"
  , "R:凄いキャラ5"
  , "R:凄いキャラ5"
  , "R:凄いキャラ5"
  , "R:凄いキャラ6"
  , "R:凄いキャラ6"
  , "R:凄いキャラ6"
  , "R:凄いキャラ6"
  , "R:凄いキャラ6"
  , "R:凄いキャラ6"
  , "R:凄いキャラ6"
  , "R:凄いキャラ6"
  , "R:凄いキャラ6"
  , "R:凄いキャラ6"
  , "R:凄いキャラ7"
  , "R:凄いキャラ7"
  , "R:凄いキャラ7"
  , "R:凄いキャラ7"
  , "R:凄いキャラ7"
  , "R:凄いキャラ7"
  , "R:凄いキャラ7"
  , "R:凄いキャラ7"
  , "R:凄いキャラ7"
  , "R:凄いキャラ7"
  , "R:凄いキャラ8"
  , "R:凄いキャラ8"
  , "R:凄いキャラ8"
  , "R:凄いキャラ8"
  , "R:凄いキャラ8"
  , "R:凄いキャラ8"
  , "R:凄いキャラ8"
  , "R:凄いキャラ8"
  , "R:凄いキャラ8"
  , "R:凄いキャラ8"
  , "R:凄いキャラ9"
  , "R:凄いキャラ9"
  , "R:凄いキャラ9"
  , "R:凄いキャラ9"
  , "R:凄いキャラ9"
  , "R:凄いキャラ9"
  , "R:凄いキャラ9"
  , "R:凄いキャラ9"
  , "R:凄いキャラ9"
  , "R:凄いキャラ9"
  , "R:凄いキャラ10"
  , "R:凄いキャラ10"
  , "R:凄いキャラ10"
  , "R:凄いキャラ10"
  , "R:凄いキャラ10"
  , "R:凄いキャラ10"
  , "R:凄いキャラ10"
  , "R:凄いキャラ10"
  , "R:凄いキャラ10"
  , "R:凄いキャラ10"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ1"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ2"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ3"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ4"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ5"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ6"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ7"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ8"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ9"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
  , "N:ただのキャラ10"
];
function gacha() {
  console.log(charList[Math.floor(Math.random() * charList.length)]);
}

module.exports = {
  gacha
};

試しに10連を3回、回してみた

スクリーンショット 2024-08-20 152705.png

結果的には以下になりました。

結果
■4回出た
N:ただのキャラ1

■2回出た
N:ただのキャラ2
N:ただのキャラ7

■3回出た
N:ただのキャラ3
N:ただのキャラ4
N:ただのキャラ6

■1回出た
N:ただのキャラ8
N:ただのキャラ9
R:凄いキャラ1
R:凄いキャラ3
R:凄いキャラ5
R:凄いキャラ8
R:凄いキャラ9
R:凄いキャラ10
SR:とっても凄いキャラ2
SR:とっても凄いキャラ3
SR:とっても凄いキャラ4
SR:とっても凄いキャラ5
SSR:なんかとっても凄いキャラ3

大体10回のうちの半分がNで、2~3がR、1~2がSRで、SSRが20回転しないうちに出たので、
割と良心的なガチャなんじゃないかなと(ほぼ運だけど

ただコレだと、ガチャで出てくるキャラのデータがアホほど多くなって可読性に欠けるため、もう少し仕組みを考えてみる。

ランク毎のキャラ一覧生成してから割り出すやつ

gacha2.js
// ランク一覧
const rankList = [
  {
    rank: "SSR",
    weight: 1,
  },
  {
    rank: "SR",
    weight: 5,
  },
  {
    rank: "R",
    weight: 10,
  },
  {
    rank: "N",
    weight: 20,
  }
];
// キャラ一覧
const charList = {
  "SSR": ["なんかとっても凄いキャラ1"
    , "なんかとっても凄いキャラ2"
    , "なんかとっても凄いキャラ3"
    , "なんかとっても凄いキャラ4"
    , "なんかとっても凄いキャラ5"],
  "SR": ["とっても凄いキャラ1"
    , "とっても凄いキャラ2"
    , "とっても凄いキャラ3"
    , "とっても凄いキャラ4"
    , "とっても凄いキャラ5"],
  "R": ["凄いキャラ1"
    , "凄いキャラ2"
    , "凄いキャラ3"
    , "凄いキャラ4"
    , "凄いキャラ5"
    , "凄いキャラ6"
    , "凄いキャラ7"
    , "凄いキャラ8"
    , "凄いキャラ9"
    , "凄いキャラ10"],
  "N": ["ただのキャラ1"
    , "ただのキャラ2"
    , "ただのキャラ3"
    , "ただのキャラ4"
    , "ただのキャラ5"
    , "ただのキャラ6"
    , "ただのキャラ7"
    , "ただのキャラ8"
    , "ただのキャラ9"
    , "ただのキャラ10"
  ],
};

function gacha() {
  const chList = rankList.map(rank=> {
    const cList = [];
    for(let i = 0; i < rank.weight; i++)
      cList.push(...charList[rank.rank].map(ch => `${rank.rank}${ch}`));
    return cList;
  }).flat();
  console.log(chList[Math.floor(Math.random() * chList.length)]);
}

module.exports = {
  gacha
};

かいせつ

ランク情報に重みを付けて、
ランクに紐づくキャラリストを検索用のListに重み分追加していき、
ソレを最後に抽出してるって感じです。
→SSR:なんかとっても凄いキャラ1はList上に1しか無いけど、N:ただのキャラ1はListに20いる感じ

ロジック内でデータを生成しているためキャラ情報分のコード量はだいぶ減ったかと。
使ってるデータ的には最初に書いたロジックと同じ【ような】データとなっている。
※キャラのインデックスが違うだけで、中身をソートしたら同じデータとなる

おまけ

SSR/SRのキャラをもっと絞りたいってなったら、ソレ以外のランクの重みを増やせば全体数が上がって、ジャブジャブ課金させることが出来るね(ニッコリ
※クソ運営という批判がバンバン来て低評価の嵐になりそうだけど。。。

ちなみに排出率を調べたい用の関数も残しておきますね

排出率チェック(ラング毎に1キャラ値の排出量を算出)
function rate() {
  const totalChar = rankList.map(rank => charList[rank.rank].length * rank.weight).reduce((total, val) => total + val, 0);
  console.log(`総キャラ数:${totalChar}`);
  rankList.forEach(rank => console.log(`${rank.rank}${(rank.weight / totalChar * 100).toFixed(2)}%`));
}
// こんな感じで出力
// 総キャラ数:330
// SSR:0.30%
// SR:1.52%
// R:3.03%
// N:6.06%
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0