キノコが何か作るブログ

ゲーム・ドット絵・アプリなどを作ります

MENU

描いた絵のアスペクト比を直す

前回描いた絵をゲームに使おうとしたんですが重大なことを忘れていました。

アスペクト比です。

最近のゲームはスマホに移植することも考えて16:9で作られることが多いので、自分もそれに倣ってアスペクト比を16:9にしようと考えていたのですが、Twitterの見栄えを気にして253×253で作っていたことをすっかり忘れていました。

ということで修正しました。

 

これが

f:id:mizukinoko:20210201222730p:plain

↓こうなりました

f:id:mizukinoko:20210201222632p:plain

サイズは384×216です。8の倍数でかつ16:9です。多くのデバイスの解像度が8の倍数なのでそれに合わせました。

 

Twitterの劣化が少なくて済むのは1:1ですが、これからは多少劣化しても16:9で描こうかなと思っています。

今回の作業をするまでは「別に横に引き伸ばすだけだし大したことない」と思っていましたが、やってみると結構時間がかかってしまいました。

でも楽しかったです。

 

明日はUnityにこれを実装して、各シーンに遷移できるようにするところまでやろうとおもいます。

それでは。

ポーション屋さん

ポーション屋さん

f:id:mizukinoko:20210127225704g:plain

連作です。

自作ゲームの雰囲気を決めるために描いています。

Twitterの反応見る限りこの方向性で正解っぽいですね。すこぶる反応がいいです。

正直絵の方はいいねが付かなくても割とすぐ立ち直れますが、ゲームの方はコケるときついので割と慎重になっています。

素材の差し替えって一番きついので出来るだけ変更したくないんですよね。。。

 

とりあえずこの路線で作って行こうと思います。

それでは。

Unityへの移行作業

公式チュートリアルを参考にした移行作業

今まで使っていたRot.js(ダンジョン生成のライブラリ)がUnityでは使えないので、公式チュートリアルを参考にマップを作ってみました。

learn.unity.com

 

f:id:mizukinoko:20210105152437p:plain

この公式チュートリアルなんですが、結局一つだけ部屋を生成してそこに障害物を置くだけみたいです。僕がイメージしているローグライクと違いました。

初心者向けのチュートリアルですからあえて簡素な作りにしたのかもしれません。

もしかしたらオブジェクトの置きすぎで処理落ちするのを嫌ったのでしょうか?

まあ床張りで1ブロックずつPrefab置いたら処理落ちするかもしれません。C#はJavaScriptより遅いらしいですから。

でもどうせだったら不思議のダンジョンっぽいのをやってほしかったです。

 

やっぱり自分でダンジョン生成を書く必要があるようなので書きました。

前回は「区域分割法」と「2分割法」というものでダンジョンを生成していましたので、今回は「穴掘り法」というもので自動生成してみました。

f:id:mizukinoko:20210106203955p:plain

中央にある草ブロックは気にしないでください。

 

穴掘り法だけだとこういう迷路みたいなのができます。

「できます」とかさも知っていたかの様に言っていますが、実行するまで気が付きませんでした。

穴掘り法は、

  1. ランダムに進む方向を決める
  2. その先の2マス先のブロックが通路か確かめる
  3. 通路でなかった場合はその方向に1マス掘る
  4. 掘った地点に移動する

これを永遠と繰り返します。

これでもいいかな〜と思いましたが、やっぱり僕は不思議のダンジョン系のゲームが作りたかったので、自分でプログラムを書くことにしました。

領域分割

立命館コンピュータクラブの領域分割とゲームプログラマーを目指すひと ランダムダンジョン生成プログラムのアルゴリズムを参考に領域分割までは作りました。

www.rcc.ritsumei.ac.jp

rudora7.blog81.fc2.com

f:id:mizukinoko:20210109225325p:plain

領域分割線の部分にブロックが配置されていないのは、そこが通路になる可能性があるからです。

分割した領域の中に部屋を配置して、分割線まで通路を伸ばします。

分割線に通路をつなぐことで全部の部屋をつなげられます。

通路としていらない分割線は削除してブロックを配置します。

いらない分割線を消すアルゴリズムはもう考えてあるのですが、たぶん何かしらのバグがある気がするのでちゃんと完成してからお披露目します。

 

現時点でこれめちゃくちゃ処理落ちしそうなんですけど、大丈夫ですかね?

まあ作るしか無い。処理落ちしたらその時考えよう。

それでは。

Unity1Weekに参加した

Unity1Weekに参加した

なんとか間に合いました。

短いです。30秒で終わります。

unityroom.com

一応ランキングも実装しました。まあアセットを入れるだけで出来ちゃうんですけどね。本当にUnityは何でも揃っています。

音源はフリー素材のものをお借りしました。

 

月火はサボって何もしなかったので、実質5日で作ったようなものです。Unity5Daysです。

 

今回は初参加なので要領がなにもわかりませんでした。

実はイベントの参加方法もよく知らなくて、Unityroomの方でイベントに参加する設定をしなければならないことすら知りませんでした。

タグをつければ参加できると思っていました。

でも、なんとか無事に間に合ってよかったです。

 

このままだとまずいので作りながら考える

Unity1Week

とにかく何も思いつかない。

自分の発想力が乏しいことを自覚する。

流石に水曜になってまだ何も手をつけていないのはまずいので、作りながら考える方向にシフトします。

 

とりあえず素材を二つほどサクッと作り、

それをUnityにブチこんで横にスライドさせてみる。

f:id:mizukinoko:20201223213754p:plain

これが雛形。ここから色々と付け足して見栄えを整える。

今回お題が「あける」なので、宝箱を空ける要素を追加。

鍵の素材を作って、空けられるようにしました。

Gifを貼ろうと思いましたが、完成してからの楽しみがなくなると思ったのでやめておきました。

完成したらまた記事を書いて、ゲームのURLを公開します。興味のある方はその時に遊んでやってください。

Rigidbody

GetComponentでRigidbodyと書いてしまうと3Dの方を取ってきてしまうらしいです。

なので、Rigidbody2Dと書かないとAddForceした際に怒られます。

There is no 'Rigidbody' attached to the "KeyGreen(Clone)" game object, but a script is trying to access it.
You probably need to add a Rigidbody to the game object "KeyGreen(Clone)". Or your script needs to check if the component is attached before using it.

こんな感じで怒られます。

この手のエラーはjavascriptで散々鍛えられていたので別にいいんですが、ちょっと不親切すぎないか?(クレーマー)

まあUnityは3Dゲームを作るやつだと思ってるので、これがデフォルトで正解だとは思います。

でもせめてコンソールにエラー吐いてほしい。

Unity8日目:完成

完成しました

リザルトシーンにスコアを表示するようにして、タイトル画面にも戻れるようにしました。

あとBGMをつけて、タイトル画面で音量調整もできるようにもしました。

f:id:mizukinoko:20201218143145g:plain

SEを鳴らす時に手間取った

PlayOnAwakというのにチェックがついていると、自動でSEが再生されてしまうと言うことを知らなくて、てこずりました。

あとはAudioSourceコンポーネントを追加しないと音が鳴らないとか、色々と時間がかかってしまいました。

コルーチン

タイトルからゲームシーンに飛ぶまでに少し間隔を空けたかったので「コルーチン」というやつを使ってみました。

やることはsetTimeoutみたいな感じなんですが、IEnumeratorという変数を返す関数を引数に渡してあげないといけません。

調べたけどよくわかんなかったです。

処理を一時停止させるにはWaitForSecondsで秒数を返すんですが、その戻り値がIEnumerator型なのかな?と思っています。

UnityRoomに投稿してみる

Unity1WeekではWebGL形式でビルドして提出することになっていますので、その予行演習として投稿してみました。

ビルドはこちらを参考にしました。

blog.naichilab.com

WebGLをインストールしてなかったのでインストールしたんですが、なぜか「No module〜」と出てWebGLが使えませんでした。

まあ再起動したら直りましたけど、なんか気持ち悪いです。

ビルドするとキャラが消える

ビルドしたらなぜかプレイヤーが消える現象が発生しました。

これが本当にわからなくて、3時間くらいかけてやっと修正できました。

原因なんですが、ビルドするとプレイヤーの座標だけ画面外に飛ばされていて、それで消えてるように見えていただけでした。

なので座標を修正して、ビルドしなおしたら表示されるようになりました。

完成品

UnityRoomで遊べるようにしました。しょぼいゲームなので期待はしないでください。

unityroom.com

不安しかないですが、Unity1Weekもがんばります。

それでは。

Unity6日目:バグの原因がわかった

staticのエラーを解決

他のstaticでないメソッドにアクセスする際はちゃんと参照取らないとダメだよ。っていうことを今日になってやっと理解しました。

//これは動かない
ScoreManager.ScoreUpdate(myPoint);

//これなら動く
GetComponent<ScoreManager>().ScoreUpdate(myPoint);

きちっとインスタンスかしたオブジェクトを参照しやがれ。ということみたいです。

ちなみに別オブジェクトのスクリプトを参照する場合は、下にも書いていますがオブジェクトを参照してからじゃないと動かないです。

エラーが出るとその先は実行されない

Unityはエラーが出た箇所から先のプログラムは飛ばされるらしいです。

JavaScriptの場合はエラーが出たらそもそも動かないか、もしくはその部分だけ飛ばして次のコードは実行されるので、Unityもそうだと思っていたが違った。

オブジェクトが参照できない

NullReferenceException: Object reference not set to an instance of an object

2日前からよくわからなかったエラーですが、やっと解決しました。

別のオブジェクトに付いてるScriptの関数を実行しようとして、このエラーが出てきました。

間違え

Scriptを取得するために書いたのが下のコードです。

ScoreManager sm = GetComponent<ScoreManager>();

これではダメで、別のオブジェクトに付いているScriptを参照する場合は、そのオブジェクトを取得してからでないとScriptを取得することができません。

完成

//完成版
//Scriptが入っているオブジェクトを取得
GameObject scoreManagerObj = GameObject.Find("ScoreManagerObject");
//オブジェクトからScriptを取得
ScoreManager scoreManager = scoreManagerObj.GetComponent<ScoreManager>();
//Scriptを実行
scoreManager.ScoreUpdate(myPoint);

f:id:mizukinoko:20201216190345g:plain

文字をデカくすると消える

Overflowというところをいじると、枠を飛び出しても消えずに表示されるようになります。
f:id:mizukinoko:20201216150245p:plain

Unityの勉強が間に合わない

1week で馬鹿にされるのが見える。

ぶっちゃけUnity始めて1週間でGameJamに参加するのって明らかに無謀な気がする。

やるまえは普通にいけると思ったけど、やってみたらやっぱり無理そうだわ。っていうのが僕の人生の大半を占めている気がする。

まあ1week やりますけどね。ただこっそりやります。だって恥ずかしいから。

宣言したのにやらないのは数少ない読者に失礼ですし。

あと5日で始まりますが、せいぜい頑張ります。

Unity5日目:一歩も前に進めない

スクリプトクラスが見つからない

Can't add script component 'TextManager' because the script class cannot be found. Make sure that there are no compile errors and that the file name and class name match.

 

スクリプトクラスが見つからないため、スクリプトコンポーネント「TextManager」を追加できません。 コンパイルエラーがないこと、およびファイル名とクラス名が一致していることを確認してください。

 

スクリプトを追加しようとしたらこのようなエラーが出て追加できなくなりました。

最初はクラス名を書き損じたんだと思い確認しましたが、別におかしいところはなく、クラス名とファイル名は一致していました。

その次にUnityサイドの調子が悪いのではないかと思い、再起動しましたがこれでも直らず。

 

それで新規でスクリプトファイルを作り、一切何もいじらずに追加してみたのですが、これでも同じエラーで弾かれました。

何もいじってないので、クラス名とファイル名が違うわけもないですし、スクリプトファイル自体に問題があるはずもありません。

非常に悩みました。

原因

結論から言うと、すでに追加しているスクリプトでバグがあってエラーを吐いている場合は、新規のスクリプトを追加することができなくなるようです。

ちゃんと元から追加していたスクリプトを修正したら、新規のファイルを追加できるようになりました。

Inspectorにpublic変数が表示されない

スコアテキストをスクリプトで更新させたいので、public変数にしてそこにTextUIを入れたかったんですが、なぜか表示されませんでした。

これはいまだに理由がわかりません。

static変数は表示されないという記事を見てstaticを取ったのですが、それでも出てきませんでした。

明日はこの問題をどうにかしたいです。

Unityの勉強:4日目

今日やったこと

  • ポーションの種類を増やした
  • 別クラスの変数と関数の呼び出し(途中)
  • スコアを実装しようとした(途中)

f:id:mizukinoko:20201214215208g:plain

別クラスを参照できない

エラーでまくって別クラスを参照できませんでした。

staticエラーぽいんですが、static付けても解決しないので今日中には終わりませんでした。

正確にはstaticつくたら動くようになったんですが、なぜか別クラスに渡す引数がNullになって渡せないんですよね。

 

明日はこの問題を解決してスコア表示できるようにしたいです。

それでは。

Unityの勉強:3日目

今日やったこと

  • 一定時間ごとにアイテムが降ってくるようにした

  • 当たり判定を付けた

f:id:mizukinoko:20201213210059g:plain

Prefabを使う

以前にUnityちゃんを動かした時もやったんですが、完全に忘れていました。
まあ最後にUnityを触ったのが5ヶ月くらい前なので、そりゃあ忘れてるわなって思いました。

インスタント生成のソースコードはこんな感じになりました。

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

public class PotionManager : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        InvokeRepeating("makePotion", 0.0f, 1.0f);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void makePotion()
    {
    	GameObject potion = (GameObject)Resources.Load("OrangePotion_lv1");
    	//インスタンス生成
    	Instantiate(potion, new Vector3(Random.Range(-5, 5) + 0.5f, 3.5f, 0.0f), Quaternion.identity);
    }
}

空のGameObjectを作って、そこにScriptを貼り付けて自動生成させました。
あんまりスマートじゃない気がするんですが、みんなそうやってるみたいなのでとりあえず考えないことにします。

当たり判定

今日一番時間がかかったのが当たり判定です。
Rigidbody2DがついていないとOnCollisionEnter2Dが呼ばれないということを知らなくてずっと悩んでました。
アタッチできてないのかなと思って何度もPrefabを作り直していました。

当たり判定のコード

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

public class HitPotion : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
    	Debug.Log("ぶつかりました。削除します");
    	Destroy(this.gameObject);
    }
}

先日作った落下してくるポーションの動きを制御するPotion.csに当たり判定のコードを追加しようと最初は思ったんですが、Unityでは当たり判定とかのスクリプトは別ファイルに分けるのがいいらしいので分けて書きました。

Unityでは一つのオブジェクトに複数のスクリプトファイルを追加できるので、こういう汎用性のあるものは使い回しができるように分けた方がいいよ。ってことなんだと思います。たぶん。

今後の目標

12月21日にUnity1weekという1週間でゲームを作るイベントがあるのですが、それに参加したいと思っています。

そのためにも毎日Unityに触って慣れておきたいです。

できればUnity1week前にミニゲームを一つ完成させて、Unityでのゲーム作りの流れを掴んでおきたいです。

それでは。

Unity2日目

1マス移動

transform.positionを使ってプレイヤーを1マスずつ移動させました。

f:id:mizukinoko:20201212121459g:plain

僕はぬるぬる移動するより1マスずつ動くゲームが好きなのでこうしました。

transform.position

これマジでなんやねんって思いました。

というのも、transform.positionは直接値を書き換えられない仕様があるんです。

Vector3 pos = this.gameObject.transform.position;

//これOK
this.gameObject.transform.position = Vector3(pos.x + 48, pos.y, pos.z);

//これはダメ
this.gameObject.transform.position.x = pos.x + 48;

マジかって思いましたね。

まあ公式ドキュメント見てない僕が悪いんですが、でもUnityのドキュメントってデフォルトの表示サイズがバグってるのでめんどくさいんですよね。だから見たくない。

バグ

Sceneタブに何も表示されなくなるバグが起こりました。
f:id:mizukinoko:20201212220440p:plain

選択状態でフォーカスし直したら直ると思ったのでやりましたが直らず。
再起動したりもしましたがダメで、ダメ元でSceneタブを複製したら直りました。

このバグの解決にわりと時間を食ってしまいました。

InvokeReapeating

setIntervalみたいな関数ないのかなと思って探して出てきたのがInvokeReapeatingです。

引数に実行関数・開始時間・インターバル時間を入れると使えます。

f:id:mizukinoko:20201212212200g:plain

これでポーションが一定間隔で降ってくるオブジェクトを作りました。

ソースコードはこんな感じです。

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

public class Potion : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        InvokeRepeating("MovePotion", 0.0f, 1.0f);
    }

    // Update is called once per frame
    void Update()
    {

    }

    private void MovePotion(){
        Vector3 pos = this.gameObject.transform.position;
        this.gameObject.transform.position = new Vector3(pos.x, pos.y - 1, pos.z);
    }
}


少ないですが今日やったことは以上です。
また明日もがんばります。
それでは。

今日からまたゲーム制作の日誌を書きます

Unityでゲーム作りたいって5000億回くらい言ってますが、いまだに作れていません。

まあphina.jsでも作れてないですが、あっちは完成しないだけで作業自体はやっています。ですが、Unityの方はまったく手を付けていません。

今までこのブログで何度も「Unityでゲーム作る」「Unityに移行する」と宣言しているのにこういう態度は良くないのではないかという思いが強くなってきました。

なので今度こそUnityを頑張ろうと思うのですが、普通にやっていたら確実にまた三日坊主になってしまうので、今日からまたゲーム制作の記録を付けていきたいと思います。

今日やったこと

スプライトシートを読み込んで操作できるようにした

  • SpriteEditorの使い方
  • アニメーションの作り方
  • 十字キーでの移動

Rigidbodyを使っています。普通の横スクロールゲームを作る時にRigidbodyを使うのはあまり賢い方法では無いのかもしれませんが、とりあえず簡単そうだったのでこれで行きます。

f:id:mizukinoko:20201211113650g:plain 

Rigidbody2dというコンポーネントがあるんですが、これのスペルがややこしくてよく間違えました。

RigidBodyではなくRigidbodyなんですよね。

bodyが小文字なんです。

KeyCodeとかSpriteRendererとかは大文字なんですが、こいつだけなぜか小文字です。

Rigidbodyは単語扱いってことなのかな?だとしたらなんでKeyCodeは大文字なんですかね。

 

 

何を作るかはまだ決めれてません。

タイトル画面、メイン画面、リザルト画面の3つで構成される短いゲームにしようとは思っています。

それでは。

phina.jsでAsepriteのJSONを使ってみる

phina-aseprite-loaderを導入してみました。

pentamania.hatenablog.com

 

  • Scriptの読み込み順を間違えた
  • phina.jsを最初に読み込まないとダメなのに、loaderから読み込ませてしまった。
  • assetsの中にasepriteを書かないとダメ
  • 素材のパスを間違えた

いろいろやらかしましたが、なんとかできました。

f:id:mizukinoko:20201201221159g:plain

すごく便利です。Asepriteでアニメーションの動きを確認してからPNGとJSON両方書き出してくれるので、楽チンです。

phina-aseprite-loaderの使い方

ググればいくらでも出てきますが、Asepriteのバージョンが違ったりしてすこし戸惑ったので、一応書きます。

タグをつける

フレームを全部選択して右クリックします。

そしたらメニューが表示されますので、「New Tag」を選びタグを付けます。

f:id:mizukinoko:20201201221653p:plain

Exportする

Asepriteの機能を使ってJsonとSpriteSheetを書き出してもらいます。

「File」からExport Sprite Sheet」を選択します。

f:id:mizukinoko:20201201221918p:plain

すると、こんな感じの画面が表示されます。

f:id:mizukinoko:20201201222046p:plain

ここで書き出すファイルの設定を行います。

Sheet TypeはPackedでもなんでもいいと思います。ただし、Open Sprite Sheetにチェックを入れないと書き出せないので注意してください。

次にSpriteの設定を行います。

f:id:mizukinoko:20201201222313p:plain

ここの「Frames:」で先ほど設定したタグを選択します。

歩行グラなど複数のシーンがある場合は「All Frames」でもOKです。

タグ付けをしていれば、Jsonファイルに書き出す際に自動で分けてくれます。

次がOutputです。

f:id:mizukinoko:20201201222809p:plain

「Output File」と「JSON Data」にチェックを入れます。

Meta情報は初期設定のままで大丈夫です。

あとはExportを押して書き出します。

assetsに登録する

書き出されたPngファイルとJsonファイルをゲームのディレクトリまで持って行ったら、phina.jsのassetsに登録します。

PNGファイルは普通に「image:{~}」に登録すればいいんですが、JSONファイルはasepriteのオブジェクトを作ってそこに登録します。

f:id:mizukinoko:20201201223238p:plain

コードを追加する

一例ですが、こんな感じで追加します。

let bomb = Sprite('bombEffect', 48, 48).addChildTo(this);
bomb.setPosition(x, y);

let bombAnim = FrameAnimation('bombSS').attachTo(bomb);

//連続再生する場合は↓を使う
bombAnim.setNext("bomber", "bomber");

//連続再生しないなら↓だけでいい
bombAnim.gotoAndPlay("bomber");

 

最後にindex.htmlにphina-aseprite-loaderのプラグインを読み込ませんます。

<script src='https://cdn.jsdelivr.net/npm/phina-aseprite-loader@latest/dist/phina-aseprite-loader.min.js'>

必ずphina.jsを読み込んだ後に書いてください。phina-aseprite-loaderはphina.js前提で動いているので、先に読み込ませるとエラーで止まります。

終わり

もっと早く導入すればよかったと後悔しています。

クソみたいなバグと戦い続けた

作ろうとした機能

トルネコとかのローグライクゲームは、起きてランダムに動き回っている敵と眠っていて近づくまで動かない敵がいます。

それにならって、プレイヤーが一定範囲内まで近付かないと敵が気付いて襲ってこない機能を作ろうと思いました。

 

今までは、敵はずっとこちらに気付いていて一直線に走ってくる状態だったんで、それが嫌だったんです。

バグ

上記の機能を付け足すとバグりました。

移動キーを一回入力するだけで壁にぶつかるまで移動してしまいます。

その上、マップスクロールすら行われず、画面外に出てしまいます。

f:id:mizukinoko:20201020084449g:plain

このバグですが、何回検証してもバグの動作が確定しなくて、ソースコードのどの部分が原因なのかすら分からずにいました。

 

付け足した機能を消せば元に戻りますが、それではいつまで経っても完成しないので、新しい機能がソースコードのどの部分に干渉しているのかを突き止める必要があります。

いろいろ探し回っているうちに1ヶ月くらい経ったのですが、今日とうとう見つけました。

f:id:mizukinoko:20201025122948g:plain

この映像見たときピンときましたね。

  • 最初はマップスクロールしたのにその次はしてない。
  • マッピングのバグと入力バグがあるのに、通路の途中で止まる。

あっこれプレイヤーのTweener処理終わる前にキー入力入っちゃってる!

たったこれだけのことでした。

 

バグの動作が安定しないのは、入力のタイミングがバラバラだからです。Tweener処理が終わった後コンマ数秒遅延が発生することがあるのですが、その遅延が一定じゃないんです。CPUの状態でだいぶ変わります。遅延することもあればしないこともあって、そのせいで動作が安定してませんでした。

 

クソみたいなバグです。

気が狂いそう。

マジでなんで気がつかなかったんだろう。

 

なにはともあれやっとできました。

f:id:mizukinoko:20201025190257g:plain

プレイヤーは行動してますが、左上の敵は移動してません。敵がプレイヤーを見つけられていないからです。

これが作りたかった。

 

残り1週間もないですが、10月で完成させる予定なので階層を10層減らしてなんとか完成まで漕ぎつけようと思います。

プライバシーポリシー