読者です 読者をやめる 読者になる 読者になる

developer_RYO’s blog

スマホアプリやPCゲーム、音楽やイラストを自由気ままに作っている人の技術ログです

開発メモ 自作したパズルアプリの抱える問題点と改善方針

スライドパズルを作成したのですが、友人に見せたところ不評でした。
ダメなところの記録をおきます。

▪️完成形がわからない
目的がわからない。例えば、パズルゲーのキャンディークラッシュは「得点⚪︎⚪︎以上」「ゼリーを全部消す」などの目的があるけど、僕のゲームには目的がわかりにくい。
→ゲーム開始時に、クリア条件、パズルの完成形を表示するような演出をつける。

▪️ゲーム性がない
制限時間も、移動回数制限もない。ただ揃えるだけのパズル。
→制限時間を設定する。あと、簡単に揃えられる必殺技的なものを使用できる代わりに、
制限時間を消費するような仕組みがあるといいかも。
クリアごとに制限時間を回復する。何面まで進めるかのスコアアタック。

▪️デジタルゲームにする意味があるのか(一番辛い)
→物理法則を無視するような動き、魔法のようなエフェクトをつけるといいんじゃないかという意見。

▪️BGMがダサすぎる
→パズルを連想させるような曲?を作る
→その前に、世界観を設定したほうがいいかも(キャラクターがいるだけでも変わりそう。)
→ゲームのテーマを決めれば、BGMも迷わず作れるのではないか?

▪️実機で遊べない。実機でテストしようとすると出るバグがまだ修正できない
→優先度高めで修正する。

Unityでのゲーム開発メモ 曲の演奏時間に対して進捗を表すプログレスバー

f:id:developer_RYO:20160102230346g:plain


gif画像にある通りの、プログレスバーを作成しました。
音ゲーの曲の進捗を表現するのに作成しましたが、ほかのゲームでも応用できそうなので、公開しておきます。

プログレスバーの作り方については、検索してみると他の方が記事にされていました。
Unityのリファレンスにもいくつかヒントが記載されています。
docs.unity3d.com

fillAmount Image.type が Image.Type.Filled に設定されている時に表示されている Image の数
fillMethod どの fill タイプのメソッドを使用するか
fillOrigin Fill プロセスの原点位置をコントロールします。各 fill メソッド毎に違う結果をもたらすことを意味します。

ここら辺をインスペクターでいじればプログレスバーは作成可能です。

■曲の長さについては、AudioClipの変数lengthで取得します。


画像オブジェクトにつけたスクリプト

//画面上部に表示するタイムゲージを表示します。
//曲の長さと、現在の経過時間より、パーセンテージを取得し、表示。
using UnityEngine;
using UnityEngine.UI;

public class BarTimer : MonoBehaviour
{
    //タイマーと、再生する曲を入れる。

    [SerializeField]
    private Timer timer;
    [SerializeField]
    private AudioClip music;

    private float musicLength;
    private Image image;

    void Start () {
        //曲の長さを取得
        musicLength = music.length;
        //画像を格納
        image = this.GetComponent<Image>();
    }

	// Update is called once per frame
	void Update () {
        //画像のFillAmountを更新し続ける。
        image.fillAmount = 1 - (timer.Now / musicLength);
    }
}

タイマーのスクリプト

//////////////////////////////////////////
//ゲームオブジェクトをタイマーにするスクリプトです。

using UnityEngine;

public class Timer : MonoBehaviour {

	/////////////////////////////////////////

	//タイマー
	private float timer;

	//タイマーを進めるかどうかを決める変数
	public bool timerRun = false;

	//タイマーをリセットする。
	public void ResetTimer(){timer = 0;}

	//タイマーを進める
	public void AddTimer(){timer +=  Time.deltaTime;}

	//プロパティで値を変えられるようにする。
	//変数をpublicにしないのは、インスペクター上で変えられないようにしたいから
	public float Now{get{return timer;}}

	///////////////////////////////////////
	//初期化
	void Start()
	{
		//タイマーの初期化
		timer = 0;
	}
	////////////////////////////////////////
	//アップデート
	void Update()
	{
		//タイマーを進めるかどうか
		if(timerRun == false) return;

		//タイマーを進める
		AddTimer();
	}
}

c#でcsvファイルを配列に格納して返すクラスを作ってみた

音ゲーのタイミングデータをCSVで取り扱いたかったので、下記のクラスを作って見ました。

StreamReaderの、closeとかdisposeとかがまだあまり理解してないので、後で修正したいと思います。

配列について、タイミングデータの数が曲や難易度によって変化することを見込んで、List<>を使用しました。


////////////////////////////////////////////////
//ファイルからノート配列を作成する役割

using System.Collections.Generic;
using System.IO;

public class InputCSV
{
	
	////////////////////////////////////////
	//データ構造

	private List<Note> noteArray;
	private int index;
	
	private StreamReader csv;
	
	
	
	////////////////////////////////////////
	//処理
	
	//コンストラクタ
	public InputCSV(string fileName)
	{
		this.noteArray = new List<Note>();
		index = 0;
		
		InputNote(fileName);	
	}
	//インデックスの最大値を返す。
	public int getIndex{get{return index;}}
	
	//CSVデータを入れたノート配列を返す。
	public Note[] Output()
	{
		return noteArray.ToArray();
	}
	
	//ファイルを読み込んで、配列に格納する関数
	private void InputNote(string filename)
	{
		//ファイルを読み込み、配列にstringを格納していく。
		csv = new StreamReader("Assets/Resources/" + filename);
		ReadAllLineCSV();
		
	}
	
	
	//CSVファイルを1行ずつ読み込み、配列に足していく関数
	public void ReadAllLineCSV()
	{
		//indexMaxの値が実際と違わないように、この関数内で初期化する。
		index = 0;
		
		//操作用データ
		string line = "";
		string[] lineArray;
		
		//順次格納
		while (csv.Peek()>=0)
		{
			//ファイルから1行読み込み、配列に格納する。
			line = csv.ReadLine();
			lineArray = line.Split(',');
			
			noteArray.Add(new Note(float.Parse(lineArray[0]),int.Parse(lineArray[1])));
			
			//行数の追加
			index++;		
		}
		csv.Close();
	}
}

ccos2dxでモーダルウィンドウを実装しました。

こんにちは。

音ゲーとは別に、パズル系のアプリを作っているのですが、
タイトル画面でボタンを押したときにモーダルウィンドウ的なものを実装したかったので、下記のサイトを参考にして
モーダルウィンドウをつくってみました。

[Cocos2d-x] モーダルレイヤを作成する | Sun Limited Mt.

▪️タイトル画面のメニューボタンなどを押したときの処理に、
auto 変数名 = ModalLayer::create();
と書けば、タッチ時にウィンドウが生成されるはずです。

▪️ウィンドウ内でのラベルやボタンの配置などは、void modalHowToPlay() や、
void modalStart()で実装を予定しています。
ゲームの難易度を設定するとき、ゲームの遊び方を説明するときなどで、
必要となるパーツが違ってくると考えております。

▪️モーダルウィンドウがどのような動き(拡大縮小とか)をして生成されるかは
modalMoving()で決めています。もし使いたい人で、動きが気に入らない人はいじってみてください。



ヘッダファイル

//モーダルレイヤーを作成します。

#include "cocos2d.h"

class ModalLayer : public cocos2d::Layer
{
public:
    virtual bool init();
    CREATE_FUNC(ModalLayer);
    
    //モーダルウィンドウが生成したときの動きを決める。
    void modalMoving();
    
    //遊びかた説明用レイヤー設定
    void modalHowToPlay();
    
    //ゲームの難易度レイヤー設定
    void modalStart();
    
    //タイトル画面に戻るレイヤー設定
    void modalBackTitle();
};

cppファイル

#include "ModalLayer.h"
USING_NS_CC;

bool ModalLayer::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    //画面サイズを取り出す。
    auto director = Director::getInstance();
    auto size = director->getWinSize();
    
    ////////////////////////////////////////////////////
    //タッチイベント取得のプライオリティ的なものを設定する。
    //これを設定しないと、モーダルウィンドウの下のレイヤーにあるものにタッチが反応してしまう。
    
    //タッチしたら全部true返すようにする。
    auto listener = EventListenerTouchOneByOne::create();
    listener->setSwallowTouches(true);
    listener->onTouchBegan = [](Touch *touch,Event*event)->bool{
        
        
        return true;
        
    };
    //プライオリティの設定。
    auto dip = Director::getInstance()->getEventDispatcher();
    dip->addEventListenerWithSceneGraphPriority(listener, this);
    dip->setPriority(listener,-1);
    
    
    //設定ここまで。
    //////////////////////////////////
    
    //モーダルウィンドウ画面を設置する。
    auto frame = Sprite::create("Modal.png");
    frame->setPosition(size.width/2,size.height/2);
    this->addChild(frame);
    
    
    ////////////////////////////////////////////
    //閉じるボタンの配置と、押したときの挙動決定。
    
    //閉じるボタンを押したときの挙動を決定。
    auto *closeItem = MenuItemImage::create("Close.png","Close.png",[this](Ref* sender){
        //ここに、閉じるボタンを押したときの挙動を書く。
        this->removeFromParent();
        
    });
    //閉じるメニュー
    auto closeMenu = Menu::create(closeItem, NULL);
    //モーダルウインドウ画像のアンカー位置+画像の縦横幅半分を加えた位置が右上になるはず。
    closeMenu->setPosition(frame->getPositionX() + frame->getContentSize().width/2,
                           frame->getPositionY()+ + frame->getContentSize().height/2);
    this->addChild(closeMenu);
    
    //閉じるボタンここまで。
    ////////////////////////////////////////////
    
    return true;
}

void ModalLayer::modalMoving()
{
    //縮小した状況から、
    //モーダルウィンドウを拡大して表示する。
    this->setScale(0.001);
    auto ease = EaseElasticOut::create(ScaleTo::create(0.37,0.7),1.8);
    this->runAction(ease);
}

早期リターンを利用してUpdateで処理させるかさせないかを管理する

こんばんは。

コーディングの勉強を本でしていたのですが、その時に早期リターンを利用すると、
コードがすっきりするという記述があったので、早速取り入れてみました。

下記は、unityで現在作っている音ゲーの、
ゲームオブジェク トにつけたスクリプトの一部

//ヒットバーの判定
void Update () {
	//先にリターン
        if(BeatNoteRun == false) return;
         
        music.checkFirst(timer.Now);
        music.checkSecond(timer.Now);
        music.checkThird(timer.Now);
        music.CheckMissing(timer.Now);
}

//音符を生成するところ
void Update () {
	//先にリターン
	if(CreateNoteRun == false) return;
	
	music.CheckCreateFirst(timer.Now,this.transform);
	music.CheckCreateSecond(timer.Now,this.transform);
	music.CheckCreateThird(timer.Now,this.transform);
}

//タイマー
void Update(){
	//先にリターン
	if(timerRun == false) return;
		
	//タイマーを進める
	AddTimer();
}

早期リターンで利用するbool型変数は、別スクリプトでコルーチンを使ってtrueにし、
ゲームを動作するようにしました。

////////////////////////////////////////
//処理

void Start () {
	audioObject.GetComponent<AudioSource>().clip.LoadAudioData();
	StartCoroutine(init(3));
}
	
	
//初期化する関数
private IEnumerator init(float waitTime){
    	yield return new WaitForSeconds(waitTime);
		
	audioObject.GetComponent<AudioSource>().Play();
	timerObject.GetComponent<MTimer>().timerRun = true;
	beatObject.GetComponent<MBeatNote>().BeatNoteRun = true;
	createObject.GetComponent<MCreateNote>().CreateNoteRun = true;
	Debug.Log("ゲームメイン初期化終了");
}


update内部をifのカッコでくくるよりかは綺麗にまとまりそうです。

field type 型名 is less accessible than field の避け方

こんにちは。

題名の件で苦しんだので、メモしておきます。

▪️問題点
変数の宣言箇所にfield type 型名 is less accessible than fieldっていうエラーが出た
出たのは、noteっていう変数のところ。

//譜面クラス
//入力タイミングと、音符のタイミングデータの結果を返します。
public class Music
{
	////////////////////////////////////////
	//データ構造
	
	protected Note[] note;
	
class Note
{
	/////////////////////////////////////////
	//データ
	
	//タイミング、サウンドタイプ、
	private float timing;
	private Sound sound;
	

▪️解決方法
Musicクラスの、publicを消したらエラーが消えた。
Microsoftのサイトになんだか書かれていたので、参考にしました。
アクセシビリティ レベルの使用に関する制限事項 (C# リファレンス)

直接基本クラスは、少なくともその派生クラスと同程度にアクセス可能である必要があります

僕の場合は派生クラスではないんですが、publicとかprivateとかをいじって、アクセスレベルってやつを合わせてあげるのが必要っぽいという認識だ。

まだまだ勉強が必要だ。

文字列型をfloat型に変換する際には、文字列に「f」をつけない

こんばんは。

CSVを読み込んで配列に変換する際、文字列を数値に変換する必要があったのですが、
その際につまづいたことがあったのでメモ。

▪️問題点
CSVを正しく読み込めない。
CSVファイルのある一行に、「1.495f,0,0」があり、文字列として読み込んで
その後float.Parse()で変換した際、Unityで下記のエラーが出た。
FormatException: Unknown char: f

▪️解決方法
CSVファイルでfloatに変換したい「1.495f」の「f」を削除した。

▪️結果
CSVを正しく読み込むことができ、変換できた。