neoacoさんの ゲーム作ったよ報告書

いつでも ねむい  げーむ ちょっとつくる

unity一週間ゲームジャム (お題:ふえる)のソースコードを公開する流行りに乗りたい

この8月にやっていたunity一週間ゲームジャム (お題:ふえる)が終わった後、ツイッターでソースを公開するのが流行っているようなので便乗してみます。

作ったゲーム

ふえるだけですむとおもうな unityroom.com
玉がどんどん増えていくブロック崩しです。
玉が増えるだけだとすぐ終わっちゃうし面白くないので、消したブロックがどんどん復活するようにしました。
復活するだけだとすぐ終わっちゃうし面白くないので、復活した奴がどんどん固くなるようにしました。全5段階。

ソース

秘伝のたれ……というほどのことでもないんだけど、オープニングとかエンディングとかその辺は見ても面白くないと思うので、
ゲームの全体をコントロールしてるところとかだけにします。

・全部で四つ
・プレイヤー(棒)、ブロック総合、ブロック個別、弾が死んだと判断するところ
・頑張ったのはブロック関連

player.cs(棒)
------------
#pragma warning disable 649
// SerializeFieldを使ってると、使ってるのに使ってないとうるさいので、
// ビルドする前にこれをつけることにしている

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class playerCtrl : MonoBehaviour{

    Vector3 pos = new Vector3(0,-7.15f,0);
    Animator Anim;
    bool isPlay=false;
    [SerializeField]
    GameObject DeadLine;
    [SerializeField]
    GameObject GameEndObject;
    [SerializeField]
    GameObject GameClearObject;
    [SerializeField]
    GameObject ResultBoardObject;
    [SerializeField]
    GameObject PlayerResultObject;
    [SerializeField]
    GameObject GiveUpObject;
    [SerializeField]
    GameObject GiveUpButton;
    bool CanGiveUp = false;
    [SerializeField]
    Text PlayTime;
    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    TimeSpan TotalDateTime;
    int playTime;
    GameObject[] BGM;
    [SerializeField]
    GameObject BGMobject;

    private void Awake() {
        // BGMのチェック
        BGM = GameObject.FindGameObjectsWithTag("BGM");
        if (BGM.Length < 1) { Instantiate(BGMobject); BGM = GameObject.FindGameObjectsWithTag("BGM"); }
        DontDestroyOnLoad(BGM[0]);
    }

    void Start(){
        // ボール出すアニメ
        Anim = transform.GetChild(0).GetComponent<Animator>();
        Anim.speed = 0;
        GameEndObject.SetActive(false);
        GameClearObject.SetActive(false);
        ResultBoardObject.SetActive(false);
        GiveUpObject.SetActive(false);
        GiveUpButton.SetActive(false);
    }

    void FixedUpdate(){
            pos = Input.mousePosition;
            pos = Camera.main.ScreenToWorldPoint(pos);
            pos.y = -7.15f;
            pos.z = 0;
            if (pos.x < -9) {
                pos.x = -9;
            }
            if (pos.x > 9) {
                pos.x = 9;
            }
            transform.localPosition = pos;
        if (isPlay) {
            TotalDateTime = sw.Elapsed;
            PlayTime.text = TotalDateTime.ToString(@"mm\:ss\.ff");
            if (!CanGiveUp && sw.ElapsedMilliseconds > 300000) { GiveUpButton.SetActive(true); }
            // ストップウォッチ関数最高
        }
    }

    void MoreIncrease() {
        Anim.speed /= 0.95f;
        if (Anim.speed > 5) { Anim.speed = 5; }
    }

    void GameStart() {
        isPlay = true;
        Anim.speed = 1;
        sw.Start();
    }

    void GameEnd() {
        Anim.speed = 0;
        isPlay = false;
        sw.Stop();
        // 死んだ球数を数えないようにする
        DeadLine.SendMessage("GameEnd");
        // ゲーム終了処理用UI
        GameEndObject.SetActive(true);
    }

    void GameClear() {
        Anim.speed = 0;
        isPlay = false;
        sw.Stop();
        DeadLine.SendMessage("GameEnd");
        GameClearObject.SetActive(true);
    }

    void SendScore() {
        playTime = (int) sw.ElapsedMilliseconds;
        PlayerResultObject.SendMessage("GetScore",playTime);  // 確かこれがランキングに値を渡してるんじゃなかったかな(曖昧な記憶)
    }

    public void CallGiveUp() {
        Anim.speed = 0;
        isPlay = false;
        sw.Stop();
        DeadLine.SendMessage("GameEnd");
        GiveUpObject.SetActive(true);
    }
}
blockCtrl.cs(ブロック総合)
---------
#pragma warning disable 649

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
using System.Data;

public class blockCtrl : MonoBehaviour{

    [SerializeField]
    GameObject block;
    GameObject[,] blockArray;
    int[,] statusArray;
    [SerializeField]
    Text BlockText;

    int statusA;
    int statusB;
    int statusC;

    int yoko = 9;
    int tate = 7;
    int lastBlock;

    GameObject Player;
    bool isPlay = false;

    void Start(){
        // ブロックの配置
        blockArray = new GameObject[yoko, tate];
        statusArray = new int[yoko, tate];
        Player = GameObject.FindGameObjectWithTag("Player");
        for (int xx = 0; xx < yoko; xx++) {
            for (int yy = 0; yy < tate; yy++) {
                GameObject bl = Instantiate(block, new Vector3(-8 + xx*2, 8.8f - yy*1.5f, 0), Quaternion.identity);
                bl.transform.name = xx + "," + yy;
                bl.transform.parent = this.gameObject.transform;
                blockArray[xx,yy] = bl;
                statusArray[xx,yy] = 1;
                if (yy == 0) { bl.SendMessage("UpStatus"); statusArray[xx, yy] = 2; }
            }
        }
        lastBlock = yoko * tate;
        BlockText.text = lastBlock.ToString();
    }

    void FixedUpdate(){
        if (isPlay) {
            lastBlock = 0;
            foreach (int i in statusArray) {
                if (i != 0) { lastBlock += 1; }
            }
            if (lastBlock == 0) {
                //gameclear
                isPlay = false;
                Player.SendMessage("GameClear");
            }
            BlockText.text = lastBlock.ToString();
        }
    }

    void GetDeadBall() {
        // ランダムで選んだブロックを一段階固くする
        int xxx = UnityEngine.Random.Range(0,yoko);
        int yyy = UnityEngine.Random.Range(0,tate);
        statusArray[xxx,yyy] += 1;
        GameObject UpBlock = GameObject.Find(xxx + "," + yyy);
        UpBlock.SendMessage("UpStatus");
    }

    void GetBlockStatus(string blockName) {
        // 各ブロックのステータスの管理
        // ブロックの名前は xx,yy という名前になっているので、分割してどの場所のブロックなのかを特定している
        string[] arr = blockName.Split(',');
        statusA = Int32.Parse(arr[0]);
        statusB = Int32.Parse(arr[1]);
        statusC = Int32.Parse(arr[2]);
        statusArray[statusA,statusB] = statusC;
    }

    void NoBall() {
        isPlay = false;
    }

    void GameStart() {
        isPlay = true;
    }
}
eachBlock.cs(ブロック個別)
---------
#pragma warning disable 649

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class eachBlock : MonoBehaviour{

    int status = 1;
    GameObject BC;
    string report;
    [SerializeField]
    Sprite status1;
    [SerializeField]
    Sprite status2;
    [SerializeField]
    Sprite status3;
    [SerializeField]
    Sprite status4;
    [SerializeField]
    Sprite status5;
    Collider2D col;
    SpriteRenderer spr;
    AudioSource AS;
    [SerializeField]
    AudioClip AC;

    void Start(){
        BC = GameObject.FindGameObjectWithTag("GameController");
        col = GetComponent<Collider2D>();
        spr = GetComponent<SpriteRenderer>();
        AS = GetComponent<AudioSource>();
    }

    void Update() {
        if (status == 5) {
            spr.sprite = status5;
            col.isTrigger = false;
            spr.color = new Color(1, 1, 1, 1);
        } else if (status == 4) {
            spr.sprite = status4;
            col.isTrigger = false;
            spr.color = new Color(1, 1, 1, 1);
        } else if (status == 3) {
            spr.sprite = status3;
            spr.color = new Color(1, 1, 1, 1);
            col.isTrigger = false;
        } else if (status == 2) {
            spr.sprite = status2;
            spr.color = new Color(1, 1, 1, 1);
            col.isTrigger = false;
        } else if (status == 1) {
            spr.sprite = status1;
            spr.color = new Color(1, 1, 1, 1);
            col.isTrigger = false;
        } else {
            spr.color = new Color(1, 1, 1, 0);
            col.isTrigger = true;
        }
    }

	private void OnCollisionEnter2D(Collision2D collision) {
        status -= 1;
        if (status < 1) { status = 0; AS.PlayOneShot(AC);}
        report = transform.name + "," + status;
        BC.SendMessage("GetBlockStatus",report);
        // SendMessageは一つの値しか送れないので、ブロック名(xx,yy)にステータスをくっつけてstringとして送信
    }

    void UpStatus() {
        status += 1;
        if (status > 5) { status = 5; }
        report = transform.name + "," + status;
        if (BC != null) {
            BC.SendMessage("GetBlockStatus", report);
        }
    }
}
DeadLineRepoat.cs(玉を殺すところ)
---------
#pragma warning disable 649

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class deadLineReport : MonoBehaviour{

    GameObject BC;
    GameObject Player;
    bool isPlay = true;
    int ballcounter;
    int deadBallCounter;
    [SerializeField]
    Text LiveBallText;
    [SerializeField]
    Text DeadBallText;

    void Start(){
        ballcounter = 1;
        deadBallCounter = 0;
        BC = GameObject.FindGameObjectWithTag("GameController");
        Player = GameObject.FindGameObjectWithTag("Player");
        LiveBallText.text = ballcounter.ToString();
        DeadBallText.text = deadBallCounter.ToString();
    }

	private void OnCollisionEnter2D(Collision2D collision) {
        if (collision.transform.tag == "ball") {
            if (isPlay) {
                BC.SendMessage("GetDeadBall");
                Player.SendMessage("MoreIncrease");
                ballcounter -= 1;
                deadBallCounter += 1;
                if (ballcounter == 0) {
                    //gameOver
                    BC.SendMessage("NoBall");
                    Player.SendMessage("GameEnd");
                    GameEnd();
                }
                LiveBallText.text = ballcounter.ToString();
                DeadBallText.text = deadBallCounter.ToString();
            }
        }
	}

    void popBall() {
        if (isPlay) {
            ballcounter += 1;
            LiveBallText.text = ballcounter.ToString();
        }
    }

    void GameEnd() {
        isPlay = false;
    }
}


こんなもんですかね。まるっと公開するのはちょっと恥ずかしいですな。
一番工夫を凝らしたと思っているところはブロックのステータスを受け渡しするところです。
ブロックを復活させるにはDestroyしてはいけないので、どうしたものかと考えていたんですが、
GWにゲームつくった時に文字列を分割したな、と思いだして、採用しました。
ランキング周りは作ってもらったんでナンモワカランです。
他にはボールのコントロールするやつ、ゲームの背景を設定するやつ、タイトル画面、カウントダウンのアニメにつけるやつなど、スクリプトファイル数だけで言えば16個作りました。中身がほとんどコピペのやつもある。

いや~~~昔よりはマシな書き方をしていると思いたいですけどね。
昔はひどかったからね。関数名とか何をつけていいのかわからなくてsusieとかsasoribi とかつけてたからね。
いまだに一行で「if (status < 1) { status = 0; AS.PlayOneShot(AC);}」とか書くけど、これはマジで例外で、本当に一文しかないしこれ以上発展しないやつだけです。何行にもわたってる方が見辛いやつもあるやん、こう、同じ数値が縦に並んでてほしいときとかあるやん。wasdでプレイヤーを移動させるときとかさぁ。(言い訳)


以上、独りぼっちゲーム開発のneoacoさんのソースでした。
最近は夫を巻き込みつつあるので、独りぼっちって言いづらくなってきたが、発注しないと何もしてもらえないのでやはり独りぼっちであってると思う。

FlashゲームをPhaser 3で作り直すぞ 第3回

※このシリーズは、一日3時間くらい作業した生録画的な感じでお伝えしております。

2020年6月8日

全てシヴィライゼーションが悪い。あとマイクラも。

気を取り直して作業を再開する。開発メモを残しておいてよかった、次に何をすればいいのかわかりやす~い。
つまりそろそろやりたくない詰めの作業ということです。
本日はリザルト画面を作りますわよ!

・リザルト画面を作る

というわけでリザルト画面の画像を用意。
10問答えたらゲームを中止してリザルト画面へ移行だ!
totalAnserNumとかいう苦肉の策的名前を準備して、回答するたびに+1する。
10になったらリザルト画面を表示するようにする。

// chaking()
}else if(gamePhaseNum === 2){
    // ゲーム中
    totalAnserNum += 1;
    if(totalAnserNum < 10){
       // 略
    }else{
        // 10問答えたのでリザルトへ移行
          // 表示しているものを全部非表示にする
          // 次のゲームフェイズへ移行
          gamePhaseNum = 3;
    }
}

10問答えたらすぐに結果表示してもいいけど、ここでワンクッション置いた方が丁寧な気がする。
「終了!」とか表示して、それからリザルトを表示することにした。

タイトル画面からプレイをしてみる。
おー、いいね、リザルト画面まで出来たぞ。これでほとんどできているというわけだ!!!!!

・ツイートボタンとリトライボタン

リザルトを表示したら、その内容をツイートしてもらいたい。
リトライするのにF5アタックはしてほしくない。
というわけでボタンを追加します。

※※※※
こんな面倒なことをしなくても画像ボタンでいいじゃない、と思う場合は
6/9のボタンの中身の話までスキップせよ
※※※※

こんな注釈をするくらいなので本当に本当にただの寄り道です。
ボタンを、CSSで作ったボタンにしたくないですか。
ぼくは、やりたいです。

まずはここを参考にする。
https://phaser.io/examples/v3/view/game-objects/dom-element/form-input
↑この中で読み込まれている"assets/text/loginform.html"が必要なので、GitHubで探しに行く……。
https://github.com/photonstorm/phaser3-examples/blob/master/public/assets/text/loginform.html
これこれ。
まーでもこれはログインフォーム全部なのでちょっと量が多すぎる。
欲しいところだけ書くと……

<style>
.button {
  color: #ffffff;
  background: #1b95e0;
  width: 100px;
  height: 60px;
  font-size: 18px;
  text-decoration: none;
}
</style>
<div id = "BT">
<button class="button">
  ついーとする
</button>
</div>

こんな感じ。あとは皆さんのCSS力でカスタマイズしていただければ……。僕は角を丸くしたり影をつけたりした。
リトライボタンの方は、あとでテキストとカラーをプログラムで変更することにする。
このあとはPhaserの見本通りに書き込んでいけばいい。

// preload()
this.load.html("nameform", "button.html");

// create()
element1 = this.add.dom(100, 420).createFromCache("nameform");
element2 = this.add.dom(210, 420).createFromCache("nameform");
// 色を変える
element2.getChildByProperty("className", "button").style.backgroundColor = "red";
// テキストを変える
element2.getChildByProperty("className", "button").innerText = "もっかいやる";

これでボタンが設置される。
ほかのボタンと同じようにAlphaを0にしておけば見えないし使えないようになるのでそうしておいて、リザルト画面で見えるようにすればいい。
色変えるの調べるのに手間取ったから今日はここまで。


2020年6月9日

ラジオ体操第一をすると肩が爆発しそうなくらい痛くて熱くなる。不健康の極み。
もうちょっとだけボタンの話が続くんじゃ。

・ボタンを動かせるようにする

ボタンを押して反応するようにするには、イベントリスナーを付ける必要がある。

// create()
// 昨日ボタンを書いたすぐ下に書くとわかりやすい
element1.addListener("click");
element1.on("click", function(pointer){
// やらせたい動作をここに書く
});

リトライボタンも同じように書く。
リトライボタンの方が中身は簡単、今出てる表示を全部非表示にして、タイトル画面を出して、数字をリセットするだけ。
ツイートボタンは……

window.open("https://twitter.com/intent/tweet?text= " + encodeURIComponent("ツイート内容"));

と書いておくと、新しいウィンドウでツイート画面を出してくれる。
ツイート画面っていうか、ツイッターを開いて発言状態にしてくれるっぽい。
昔と挙動が変わったな。この調子で画像添付できるようにならないかなぁ。


・できた! ガハハ!  ……と、その前に

気がかりな点が二つある。
1.得点の計算はどうする?(現状、正答=1ptとしか数えてない)
2.スコア表示のフォント……どうする?

・得点計算

Flash版と同じように、早く答えると点が高いようにしたい。
というわけで、時間の計測をすることにする。

時間の計測
問題の表示 →計測開始
左右どちらかのボタンを押す →計測終了

この時間の差を測って、得点に変換しよう。
早い方が良いってことは……1問5秒ぐらいで答えてほしいから……
5秒から引いた回答までの時間を得点にしよう。
正解しても点が貰えなかったら寂しいから、5秒以上は総じて1点だ。
時間を計測するっていうのはちょっと面倒くさくて、始めの時刻と終わりの時刻を記録して差を求めることになる。

// 時間の計測開始
timerSatr = new Date();
// 時間の計測終了
timerEnd = new Date();
// スコアの計算(ミリ秒なので5s = 5000 ms)
timeScore = 5000 - (timerE - timerS);

これらを問題表示と回答ボタンのところに書いておけばいい。
いいね~ 数字もべらぼうな数字になって楽しいな! ワハハ

・フォント

フォントねぇ……全部がGoogleWebフォントだったり、ゲームでまるっと載せていいライセンスのフォントだったりで
通信の重さとかしったこっちゃねー!状態なら日本語フォントとかそのまま入れちゃうんだけど
(参考:https://phaser.io/examples/v3/search?search=font のWebFontってかいてあるやつ)
ゲーム中の文章も数少なく全てを画像で済ませちゃったので、
スコアの表示をするためだけにフォントが必要になってくる……。
いや、全部フォントをそろえたいだけのアレなので、あれですよ、
ゲーム中のフォントをそもそもgoogleWebFontにあるフォントで作ればいいだけなので、
私がたぬき油性マジックを使いたいだけのエゴなので、
数字だけのビットマップフォントを作りました……。
(参考:http://narudesign.com/devlog/create-bitmap-font/
禁止事項に触れてないから大丈夫。たぬき油性マジックの懐の広さよ。ありがとうありがとう。
これで画像さえあればビットマップフォントが作れるようになったぞワハハ。

そんなことよりゲーム中の表示ね。
https://phaser.io/examples/v3/view/game-objects/bitmaptext/static/change-font
これかな。

フォントを読み込んで、
テキストの表記をただのテキストからビットマップフォントの書き方へ変更

// preload()
this.load.bitmapFont("tanuki", "assets/fonts/bitmapfont-export.png", "assets/fonts/bitmapfont-export.xml");
// create()
// x, y, fontname, text, fontsize
scoreText = this.add.bitmapText(315,350,"tanuki", " ", 102);

これで様子を……いいね! できたできた


完成したジャン! すごいじゃん!
やったー! やったー!


どれどれ……サーバにアップロードして……ツイートして……完成! 完成です!
やったー!

結局全部で一か月くらいかかっとるんかーーーーーい!

・出来上がったゲーム

出来たゲームはこれだ!
遊んでくれ!!!!!
neoaco.com

FlashゲームをPhaser 3で作り直すぞ 第2回

※このシリーズは、一日3時間くらい作業した生録画的な感じでお伝えしております。

2020年5月18日

毎日ゲーム作ってて気がくるくるしてきたので、しばらくゲームで遊ぶばっかりの日々を過ごした。
精神が正常化してきたので続きの作業をする。`ヮ´ワハハ

・ゲーム中のクリックを整理する

調査の結果、image.setInteractiveでボタン化したオブジェクトは、Alphaが0だと非アクティブ化することが判明。
色つきの四角を仮置きして、見えなくするために透明すると動かなくなるっていうワケ……マジ?
兎に角、この理屈がわかったことにより、ボタンの整理ができるというものだ。ワハハ`ヮ´

・スタートボタンを押す
 → 選択肢の表示
 → 全面ボタンの非アクティブ化

・左右どちらかを押す
 → ○×の表示
 → 全面ボタンのアクティブ化

・全面ボタンを押す
 → ○×の非表示
 → 選択肢の表示
 → 全面ボタンの非アクティブ化


アクティブ/非アクティブはsetAlpha()で1にしたり0にしたりすればよい。
よいな。よし。できた。

これで大まかな仕組みはできたので、しばらくは画像制作のターンとなる。
うわーめんどくさい。でも自分で準備したいから頑張るしかない。


2020年5月20日

ぜぇぜぇ、選択肢の絵を描いてきたぞ。あほげーの時、二日で作ったはずなんだけど、こんなに大量に絵を描いたって本当か? 信じられん……。

ということで絵を描いてきたら選択肢が全部で12個になったので、リストを増やしたり画像を差し替えたりウンヌンする。

ウンヌン! ウンヌン! ウンヌン!
(※効果音のみでお送りしております)

これでよし。
表示してみる。それぞれの画像をちょっと修正したい気持ちがムクムクしてくるが、今はその時ではない……。


フォントを導入したりしようかといろいろ試してみたけど、変動する表記は数字だけなので止めることにした。
日本語フォントは重いし……。
どうするのかはまたあとで考えよう。

・ゲームのトップ画面と、フェーズ移行について

ゲームの中身はできたので、そのほかの大まかな流れについて考える時が来た。

タイトル画面
↓
説明文
↓
ゲーム
↓
結果発表
↓
タイトル画面に戻る

フェーズ分けはこんな感じかな。
今はゲームの部分しかない。


シーンを増やすという手もあるが、まだよくわかってないので、フラグ管理でどうにかすることにした。
新しくgamePhaseNumというintを導入する。
これで、今どのフェーズにいるのかを管理し、全面クリックがどのような挙動をするのかを決める。

上から順番に数字を割り振って、全部で4フェーズ。
gamePhaseNumを使って、今どの場面なのかをチェックする関数を作る。

// create()
// 全面クリック用のボタンを作る
AllArea =  this.add.image(250, 250, "AllArea").setInteractive();
AllArea.on("pointerdown", function (pointer) {Checking();});

function Checking(){
    if(gamePhaseNum === 0){
        // タイトル中にクリックされたら起こることを書く
    }else if(gamePhaseNum === 1){
        // 説明文表示中に(以下略
        // ゲーム最初の設置を行う
    }else if(gamePhaseNum === 2){
        // ゲーム中に…(ここは5/18の内容が入るところ)
    }else if(gamePhaseNum === 3){
        // 結果発表……
    }
}

画面をクリックすると次のフェーズに移る感じ。
ゲーム中はAllAreaのAlphaを0にしておけば反応しない。よしよし。

いや~、今日はいっぱい頑張たな。
続きはまた明日。

FlashゲームをPhaser 3で作り直すぞ 第1回

※このシリーズは、一日3時間くらい作業した生録画的な感じでお伝えしております。

~前回までのあらすじ~

Phaserで新しいゲーム作りたいけど、その前にFlashChromeに殺されるから過去のお気に入りのゲームを移植しておこう。

移植するゲームはこれだ!

あほげー第15回 トミート High & Low
neoaco.com


・ゲーム概要
二つの選択肢のうち、より「トミー」な方を選ぶ。
全10問。


Flash版ゲームの仕様
(たしか)15種類くらいの選択肢。
ランダムに二つ提示し、よりトミーな方を選ぶと加点される。
速く判断した方が点が高い。

Flash版を作るときに頑張った点
どっちがよりトミーかを判断するために、選択肢をミートな方から順番に数値を付け、数値が大きい方がトミーとした(入れ知恵)。
選択肢を選んで、○×が出て、次の設問へ移るようにした。

この仕様を継承し、Phaserで移植……というか、作り直し!をやっていこうと思う。

2020年5月11日

思い立って開始。
やっぱHDDの中にデータ無い……。

・とりあえずの画像の作成

どうせコードで使いまわせるところなど無いので、画像は諦める。悲しい。
とりあえず、仮に数字0~9で作ることにした。
f:id:neoaco:20200610154138p:plain
これを分割して使うようにする。

・index.htmlとgame.jsの作成

チュートリアルを見ながらconfigを書けばいいのだけれど、センタリング大好きマンとしてはゲームをウィンドウのセンターにおいてほしいので

// config
autoCenter: Phaser.Scale.CENTER_BOTH,

これを追加している。
あと、index.htmlのどこにゲームを表示してほしいかということを書くために

parent: "center",

これも追加している。
表示したいdivのidをここに書く。
私はheader>center>footerという構造にしているので、"center"となる。
(※はじめはゲームだけしか表示しないから要らないじゃんと思ってたんだけど、DOMでボタンを表示しようとしたら、このparentの設定が無いとうまくいかなかった。罠。)

ついでだから、configを全部載せておこう。
このゲームは物理演算を使わないので、すでに項目から削除している。

var config = {
    type: Phaser.AUTO,
    width: 500,
    height: 500,
    backgroundColor : 0xffffff,
    autoCenter: Phaser.Scale.CENTER_BOTH,
    parent: "center",
    dom: {
        createContainer: true
    },
    scene: {
        preload: preload,
        update: update,
        create: create,
    }
};
・画像の読み込み

分割して使うので、スプライトシートとしてプリロードで読み込む。
https://phaser.io/examples/v3/view/textures/sprite-sheet
creat()でadd.imageすればよい。画面の外に表示しとこ。

tomeet0 = this.add.image(-200,-200,"tomeet",0);	
//(x,y,スプライトシートの名前,何番目の画像か)

これを9まで作って、配列に入れる。

tomeetList = [tomeet0,tomeet1, ... tomeet8,tomeet9];

これで、画像を数字で比較できるようになったぞ。

・画像の表示

取り敢えずランダムに選ばれて出てきてほしい。
概要:数字をランダムで選ぶ→その数字の絵を見えるところに置く

スペースキーで配置できるようにする。

 // create()
KeySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);

 // update()
if(Phaser.Input.Keyboard.JustDown(KeySpace)){
    LeftNum = Phaser.Math.Between(0,9);
    setTomeet(); 	// 選ばれた画像を設置する動作は外に出した
    console.log("Space is JustDown : " + LeftNum);
	// 念のために押された時に得た数値をコンソールにも書く
}

 // setTomeet()
 tomeetList[LeftNum].setPosition(50,50);

これでindex.hmtlにてスペースキーを押すとランダムに数字が出る。出るんだってば。
早くに大きい数字が出ると小さい数字は隠れてしまって出てないかもしれないけど、まぁリロードすれば確認できるからいい。ここは出てさえすればいいのだ。
(後でゲームの機能を作りこむときに作業するから気にしない)


同じように右にも出るようにする。
同じものが出たら困るので、左と同じものが出たらもう一度抽選することにする。

LeftNum = Phaser.Math.Between(0,9);
RightNum = Phaser.Math.Between(0,9);
while (RightNum === LeftNum){
    RightNum = Phaser.Math.Between(0,9);
}

はじめはwhileじゃなくてifで条件付けしてたんだけど、10個くらいの選択肢だと結構な頻度でかぶっちゃうみたいなのでwhileにした(怒)
まあこれでかぶることは無くなったのでヨシとする。



2020年5月12日

なんだこれ、クッソ暑いやんけ。夏だ。

・どっちをクリックしたんですか問題

このゲーム、
1.どっちをクリックしたのか
2.もう一方と比べて数値はどうなのか
を見極めなければならないわけで。
どうやろうかな……。
このトミートリストは数字が小さいほどトミーとするので、

右をクリックした
    → 右 < 左 なら正解
左をクリックした
    → 左 < 右 なら正解
そうでなければ不正解

という判定をすれば良さそう。
"画像とは別に"クリックする用のボタンを上から表示すればいい。
クリック用の枠(元ゲーム参照)を作って、クリックしたら上記の関数に働いてもらうようにする。
https://phaser.io/examples/v3/view/input/mouse/click-sprite

// ボタンの設置
LeftB =  this.add.image(145, 350, "button", 0).setInteractive();
LeftB.on("pointerdown", function (pointer) {answerL();});

ボタン用の関数も書く。

// answerL()
if(LeftNum < RightNum){
    score += 1;
}else{}

これで取り敢えずコンソールにでも表示しておけば、左が正解の時はスコアが増える!
よしよし。
右ボタン用を作ったり、もしアレだったらスコアの表示をつけてもいい。


今日はここまで。
あれっ……なんか作業した量のわりに全然進んでない気がするぞ。
まぁいいか。

FlashゲームをPhaser 3で作り直すぞ 第0回

※このシリーズは、一日3時間くらい作業した生録画的な感じでお伝えしております。

2020年5月11日。

この日、neoacoは慢心していた。
あほげーGW緊急開催(5/3-5/5開催)のゲームをPhaserJSで楽勝に作り上げたからだ。
この調子で古いFlashゲームを移植しよう。

ゲーム作ったときのデータがどっか行ってしまったことを思い出さなかったことにして、やりやすそうなものから手を付けることにした。


Phaserの導入は各自で何とかしてくれ。そこまで面倒みられんぞ。
公式のチュートリアルを一通り眺めたことのある人向けに書くので、チュートリアルは見ておいてね。

・よく見るページ

Phaserのトップページ
https://phaser.io/
チュートリアル(Making your first Phaser 3 gameのこと)
https://phaser.io/tutorials/making-your-first-phaser-3-game/part1
公式コードサンプル
https://phaser.io/examples
公式APIドキュメントより見やすい一覧表みたいやなつ(非公式だと思う)
https://rexrainbow.github.io/phaser3-rex-notes/docs/site/


※"var なんとかかんとか"は、基本的に全部まとめてpreload()の前に書くこととする。
 なんで一番初めに書くかというと、関数の合間に書くととっちらかるし、
 関数の位置を動かしたら定義されてなくてエラーが出るとかいうしょうもないミスをなくすためである。


では早速! 次の記事からゲームを作り始めたいと思いマス。
続く。

Unity1weekに参加したよ まとめ(お題:逆)

まぁ表題の通りなんですけど、
余力があるので振り返ろうと思います。
下の方はちゃんと技術的っぽいことを書いているぞ。読んでね。

作ったゲーム

unityroom.com


日曜日(2/23)

あほげーの打ち上げ放送のためにTOKYOへ移動
乗る新幹線を間違えるとかいう前代未聞の失態を犯す
更にケーブル忘れたりキーボードお願いするの忘れたりして
アキバで買い物 必要だったケーブル(miniディスプレイポート/HDMI)はマニアックすぎてビックカメラには無かったがヨドバシにはあった
もうずっとヨドバシさんについて行きます

月曜日(2/24)

折角だから、とTOKYO観光。
お題が発表されているのは知っているが、今回はあほげーとイベントの広報で忙しかったし今週は休みたいからパスしよう、とか考えている。
Nintendo TOKYOのTシャツほしかったけど売ってなかった
販売数限定だったの? 悲しい……

火曜日(2/25)

イクラファーミングシミュレーター、風花雪月を遊ぶ。
ゲームも思いつかないし(一応考えている)今回はパスかな~~~~~
ワッハッハ `ヮ´🎮 📺

水曜日(2/26)

( ゚д゚)ハッ!
まちがい探し……まちがい探しなら今から作っても間に合うのでは?
その名も「逆まちがい探し」!

木曜日(2/27)

思い付いてしまったので仕方なくゲームを作り始めることに。

まちがい探しなら、とりあえず表示するものが必要!
MagicaVoxelで作った方が速い。
ササっと作れるものといえば公園パック(何度も作ってるのですぐつくれる)。
木×2、銅像、噴水……というか池、花壇、ベンチ、UFO、ハト、ネコ、イヌ。あと空き缶。
イヌが抽象的な何かになった気がするがこれはイヌです。

そして

インストールってめっちゃ時間かかるんだよね。
もういいや、農業やろっと🚜

金曜日(2/28)

昨日作ったオブジェクトをUnityにインポートして、置いてみる。
まぁ、いいんじゃない?
本当はFPSで公園内をうろうろする感じにしたかったけど、パソコンがぽんこつになってきて遊ぶときに重すぎて悲しいし、なんかそこまで作るのめんどくさいなと思ったので、雑誌のまちがい探しみたいに二つの画面から選ぶだけ!のお手軽モードにすることに決めた。

まずは画面をつくる!
Inkscapeで枠を作って……カメラを二つ準備して、枠に嵌めていい感じにする。

Unityのカメラって、何を映して何を映さないのかをレイヤーで決められるので、
両方映すもの、片方にしか映さないもの、どっちにも映さないもの、の四つのレイヤーでオブジェクトを管理すればおっけー。レイヤーもスクリプトから動的に変更できるので安心。

例:
花壇.gameObject.layer = 0;
// 0はDefaultレイヤー

あたま良いな~~~。
これで同じところに置いても右と左で違うものを表示できるぞ。
左右で同じものを映した時にどうやって同じものを同じ場所に配置しようかと思ってたんだけど、同じものを映せばいいんだよ。天才だな~~~~。

池が。実際に配置してみると灰色の塊にしか見えない。
オブジェクト作り直します……。
ほかに公園っぽいものって何だろう、滑り台かな?
えっ……滑り台難しくない? 上の方とかどうなってんの? 支えは? 階段は?
うわ~~~~~こんなよくわかってないもの作り始めるんじゃなかった……(でも辞めない)

それにしても、なんか今日遅くまで作業していた割に全然進んでないんだけど何が……アッ


結構な時間まで農業やってたわ🚜


土曜日(2/29)

夫が風邪で布団から出てこないので、もくもくと作業ができる。
夫が居るなら作業している場合ではないので……。

滑り台を完成させて、配置。池よりマシ。

配置されるオブジェクト、アクセスするたびにランダムに配置されたいということで
書いてたんですよ、まぁ3状態/5種類しかないし配列使うのも面倒だからランダムとif文でいいかなって……。
あたまがこんがらがってきた
中止だ中止!

おとなしくListを種類ごとに作って、SystemとSystem.Linqをつけて
Listをシャッフルだ!
中身が! 2つ3つしかない! Listを! シャッフル!!!!!
過剰な力のようなきがするけど気にしない。

using System.Linq;
using System;
ListSame  = new List<GameObject> { same1, same2, same3};
ListSame  = ListSame.OrderBy(i => Guid.NewGuid()).ToList();

これを使うとRandomがUnityEngineなのかSystemなのかわからんってエラーが出るからランダムを書くのがめんどくさくなるんだけどListのシャッフル便利すぎて霞む。
おとなしく大量の赤波線を一個ずつ修正していく。

やったー。これでいい感じにランダムに出たりでなかったり前回とちょっと位置が違ったりして出るぞ。
あとは……どうやって左右同じの場所をクリックするか……。
とか悩む前にササっと検索。
良いのがあるやんけ!!!!

Rayを飛ばさずに簡単にオブジェクトのクリックを検知 | のっぴの備忘録

これはラクチンだな~~~~
Buttonと同じ感じで使える。
Rayをいちいち書かなくて済むし、今回みたいに複雑じゃない3Dの時はこれで一発だな。

// 2Dの時はこれで十分
void OnMouseDown(){
  クリックされたときの処理
}

いや~、できたでき……あれ? 急にクリックが効かなくなったぞ……?

これ、どうも内容はRayを飛ばして検出してるらしいんだけど、
手前にUIのimageとかあると遮られちゃうみたいなんですよね
透明かどうかは関係ない
f:id:neoaco:20200304145814j:plain
UIのどこかにある、この「Raycast Target」ってやつをOFFにすると、Rayを遮らなくなるので安心
前面にimageを貼ってる私が悪いんですけどね

全体のクリック数をカウントしたり
ゲーム開始のカウントダウンをつけたり
こまごまとしたものをつけて
とりあえずほとんどできたんですよね

なんでこんなうすぼんやりと悩んでいるかというと
まぁ手早くつくるならnaichiさんの準備してくださった
NCMBを利用したランキング設置なんですけど
【Unity、WebGL】なるべく簡単にオンラインランキング機能をつけるサンプル - naichi's lab
すげーーーーーべんりなんですけど、
アクセスがなくなると2か月でデータがなくなっちゃうんで
ちょっと悩んでるんですよね
折角遊んでもらってもいつの間にかなくなっている……
自前のサーバで何とかならないかな
その前はGoogleスプレッドシートとやる方法があったけど、
あれめちゃくちゃ遅くてちょっと戻りたくない
ねぇ、ちょっとそこのサーバのことがよくわかるさん、自前のサーバでやりたいよねぇこういうの、ねぇ、ねぇ、
夫「できなくはないけど……」
やったー次のあほげーくらいまでに何とかお願いします
(本人はもうこのやり取りを忘れていると思います)

日曜日(3/1)

やっぱりランキングはついてる方がいいよな~~~
どんなランキングにしようかな~~~
といっても、このゲーム、タイムとクリック数しか見るところないので、
それを捏ね捏ねするしかないわけで。
・時間のはやさを競うすばやさランキング
・誤クリックの回数を競う?せわしなさランキング
(正確にはせわしなさは時間当たりのクリック数をポイントのもとにしている)
の二つをつけることに。
本当は二つ並べて表示したいんだけど、そういう仕組みはないっぽいのと
そんな機能がんばってつけるようなこだわりがあるわけではないので
二つのランキングへのボタンをつけておしまい!

マジ?! 16時に投稿を終えてる?!?!

新記録なのでは?!?!?!

というわけで完成したのであった。
すばらし!!!

作ったゲーム

unityroom.com

いっぱいあそんでね!!!!!!!

Unity1week(お題:つながる)に参加しました!!!、おまけの一日

春眠暁を覚えず。
いつでもねむいneoacoです。

さて、昨日でunity1weekは一応終わったんですが、
普通にスクリプトミスしてたので修正することに。
ついでに音を付けたり、スクショとって載せたりしました。

そんなこんなで、今後重大なバグとかがなければこれ以上は触らないので、
ここまでは許して……というお気持ちです。
最後の方は作業量が多すぎてもうスクリプトも途中のスクショもないんですが、
ゲームも公開出来て、ブログも書き終えて、
いっぱい頑張ったので自分的にはとても満足です。

パーティクルとかちょっとわかってきたので、またほかのゲームでも使いたいなぁ…などなど。

あほげーの時は必死にやってるだけなので、こうやって振り返りつつ作るのもいいですね。

ではでは
ゲーム遊んでくださいね!!!!!!!!
unityroom.com