【ジョーカースクリプト勉強会】番外編・セーブデータの管理【Unity】
どうも、ふくともです。
今回はジョーカースクリプトを利用したUnityのセーブデータ保存場所の特定を行おうと思います。
当方、開発環境がMacであるためUnix系ターミナルコマンドを使用しています。
Windowsについてはそのうち別の記事に書き起こすかもしれません。
というのも、現在サークルの制作でAndroidアプリを組んでいてロード/セーブ画面で「PCでの開発中に作成されたセーブデータ」をAndroid機から参照しようとしてフリーズする、という自体に陥りまして。
セーブデータ自体をAndroid機に突っ込んでやろうかとも考えたのですが、アプリ配布に当たって既にセーブデータが入っているのも如何なものかと思い、とりあえず.apkビルド前にセーブデータを削除してしまおう、ということに。
日時を利用してセーブデータの保存先を特定したいのでとりあえずセーブデータを1つ変更しておきます。
↓ジョーカースクリプトのロード画面(カスタマイズ有)
画面から分かるようにセーブ日時も保存されているようです。
ということは、セーブデータもこの時間に変更されているはず。
Unix系の検索コマンドでこの時刻に最後の変更が加えられたファイルを検出したい。
$ find / -mtime 0
これでストレージ内のその日に変更が加えられたファイルまでは絞れるようです。
......と言ってもその日のうちに変更したファイルではきっとソースコードなども引っかかってしまい絞り切れてるとは言えませんね
先ほどのリンクをもう少し見てみると -ls オプションを加えることで詳細表示が出来るようです。
-lsオプションでは時刻も表示されるので
$ find / -mtime 0 -ls
これで必要なデータは見つかるはずです。
が、表示された中から目標のファイルをマンパワーで探すのは効率的ではないので、
を併用します。
セーブデータによると01:20:59(時:分:秒)に変更されていますね。
-lsオプションで表示されるのは時:分なので、01:20を含むものだけを表示させればいいですね。
コマンドの併用は | (パイプ記号)をコマンドの間に挿入すればできるので、
$ find / -mtime 0 -ls | grep 01:20
でOKです。
※時刻に関してはそれぞれの環境に合わせて適宜置き換えてください。
また、@saveタグの定義ファイル
Assets/JOKER/Scripts/Novel/Components/SaveComponent.cs
を見てみると
configファイルで指定されている最大セーブスロット数まで、0から順に列挙していきます。
save_0 save_1 save_2 … というセーブファイルを列挙していく
save.name:セーブファイル名(ロード時に指定する名前でもある)
とあるので、今回の環境ではセーブファイル名にはsave_4が使われているはずです。
時刻と同様にgrepを叩き
$ find / -mtime 0 -ls | grep 01:20 | grep save_4
でファイルの位置が特定出来ます。
※ディレクトリによっては権限の関係で参照できない可能性もあるので念のため管理者権限で実行したほうが良いかもしれません。
同じディレクトリに入っているのは大体セーブファイルなのでビルド前にまとめて削除or退避させてビルド後に再配置すれば実機デバッグは問題なく行えます。
以下、軽い宣伝になります。
今回使用した画像は私の所属する同人サークル「outer(base);」(読み:あうたーべーす)でAndroid移植を進めているノベルゲーム「Surface」のロード画面になります。
3月中にはリリース予定ですので、ジョーカースクリプト勉強会シリーズの制作サンプルとして遊んでみていただけると幸いです。
以上。
2016/02/12 Fri.
【ジョーカースクリプト】揺らしエフェクト改
既に年が明けてから一月と少し経っていますが、あけましておめでとうございます。
ふくともです。
以前作ったジョーカースクリプト用の揺らしタグですが、少し手を加えて振れ幅/時間に任意の値を指定できるように改良しました。
ファイルの配置位置等は以前の記事をご参照ください。
今回のタグを呼び出す際には
[quake x=◯ y=△ time=□]
と書いてください。
以下、改良後のコード
// QuakePluginComponent.cs
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace Novel{
public class QuakeComponent:AbstractComponent{
public QuakeComponent(){
this.originalParam = new Dictionary<string,string>{
{"x","1.5"},
{"y","1.5"},
{"time","5.0"}
};
}
GameObject target = GameObject.Find("MainCamera");
public override void start(){
double x = double.Parse(this.param["x"]);
double y = double.Parse(this.param["y"]);
float time = float.Parse(this.param["time"]);
iTween.ShakePosition(target, iTween.Hash("x",x, "y",y, "time",time));
this.gameManager.nextOrder();
}
}
}
GitHubからダウンロードもダウンロード可能です。
このページでのソース中で水色で書かれている部分が以前との差分になります。
まずpublic QuakeComponent(){ };の中。
this.originalParamにx,y,timeの3つの値を受け取ります。
Dictionary<string,string>は二つの文字列を格納するコンテナで、第一要素が変数名、第二要素がデフォルト値になります。
このDictionary<string,string>をList<string>に置き換えると要素は変数名だけになり、その引数が必須になります。
今回はx, y, timeの3つの引数を持ち、それぞれのデフォルト値が1.5, 1.5, 5.0、3つとも引数を与えるかは任意、ということになります。
次にpublic override void start(){ };の中。
double型のx,yとfloat型のtimeに引数で与えられた値を代入します。
Dictionary<string,string>とあるようにこの時paramに入っているデータはstringクラスのものなのでdouble.Parse(), float.Parse()でそれぞれの型に変換(キャスト)して代入します。
最後に iTween.ShakePosition( );です。
前回は固定の値を入れていたところに今回は引数を受け取った変数を使うので、値ではなく変数名を書きます。
補足。
x,yの単位はピクセルではないようです。(Unity側の仕様かと思われます)
画面中心がx==0, y==0となっていて、
目安としては
・x==15.4で画像が画面右側
・x==-15.4で画像が画面左側
・y==9.2で画像が画面下側
・y==-9.2で画像が画面上側
にそれぞれ見切れるようです。
timeには秒数を入力してください。
以上です。
2016/02/05 Fri.
【C++】素因数分解プログラムと処理時間計測【chronoライブラリ】
意外にもスパンが短く、5日ぶりです、ふくともです。
今回は、書こうと思ったものの先に前回の記事を書くべきだと思い一旦流した記事を。
なんとなくノリで作ってみたプログラムを晒してみます(実用性は気にしない)
作ったものはタイトル通り素因数分解プログラムで、入力された2以上の整数値を素因数分解します。
vectorの使い方については前回の記事で触れたため割愛します。
流れとしては
Ⅰ. 値入力
Ⅱ. 与えられた値を構成する素数の検索
Ⅲ. Ⅱで得た各素数の指数を計算
Ⅳ. 出力
です
性能テストのために処理時間も計測しています(が実用性は気にしないなら不要な気も)
以下コード(コメントも一応添えていますが解説は後述)
#include <iostream>
#include <vector>
#include <chrono>
using namespace std;
// 素数と指数をペアで格納する構造体を定義
struct KEKKA{
int Sosuu;
int Shisuu;
};
int main(void)
{
int N;
cout << "2 ≦ N=";
cin >> N;
cout << endl;
// 入力が終わったタイミングで計測スタート
auto start=chrono::system_clock::now();
if(N<=0){
cout << "ERROR";
}else if(N==1){
cout << "1^1";
}else{
// 変数宣言とか
vector<int> sosuu;
sosuu.push_back(2);
vector<kekka> kekka;
// Nを構成する素数の取得
// エラトステネスの篩
// 3以上の素数は奇数であることを利用
for(int i=3;i<=N;i+=2){
if(N%i==0){
bool flg=true;
for(int j=0;j<sosuu.size();++j){
if(i%sosuu[j]==0) flg=false;
}
if(flg==true) sosuu.push_back(i);
}
}
// 求めた各素数の指数を求める
for(int i=0;i<sosuu.size();++i){
int M=N;
int shisuu=0;
for(;M%sosuu[i]==0;++shisuu){
if(M%sosuu[i]==0)
M/=sosuu[i];
}
KEKKA tmp={sosuu[i],shisuu};
kekka.push_back(tmp);
}
// 出力
cout << N << " = ";
for(auto p=kekka.begin();p!=kekka.end();++p){
if(p->Shisuu!=0){
cout << p->Sosuu << "^" << p->Shisuu;
// kekkaコンテナの最終要素でなければ「素数^指数」の後に「 * 」を出力
if(p!=kekka.end()-1) cout << " * ";
}
}
}
cout << endl;
auto end=chrono::system_clock::now();
auto diff = end-start;
auto diff_mcrsec = chrono::duration_cast<chrono::microseconds>(diff).count();
cout << "所要時間:" << diff_mcrsec << "マイクロ秒" << endl;
return 0;
}
chrono ←処理時間の計測などに使うライブラリ
chrono::system_clock::now() ←現在時刻の取得
→計測終了時から計測開始時を引くと処理時間が得られる
※型名が長いのでautoで指定したほうがタイプミスも少なくて無難
正式にはstd::chrono::system_clock::time_point型
chrono::duration_cast<chrono::microseconds>(diff).count() ←変数diffの値をマイクロ秒の整数値に変換
変換可能な単位などはこちらを参照↓
0以下の値が入力された場合にはERRORを出力
1が入力された場合には1^1を出力
2以上が入力された場合にはその値を素因数分解して出力
ex) 入力値:15 出力:3^1 * 5^1
最初のループではNの約数の内、素数であるものをsosuuコンテナに格納する
その次のループでは得た素数で入力値を割り、割り切れればその素数の指数を1増やす
割り切れなくなったらKEKKA構造体型の変数に素数と指数をペアで格納し、その変数をkekkaコンテナに格納する
↑格納するデータが一つでない場合には一度構造体型変数に纏めてからコンテナに格納するほうが楽
「入力値= 素数^指数 * 素数^指数 * 【中略】* 素数^指数」のフォーマットで出力
以上です。
プログラムそのものは置いておくとして、処理時間の計測はそこそこに有用だと思います。
2015年の記事は今回が最後になると思います、閲覧してくださった方々ありがとうございます。
それでは良いお年を。
2015/12/27 Sun.
【C++】素数判別プログラムのサンプルとvectorの練習
ご無沙汰です、ふくともです。
入力された値を素数かどうか判別するプログラムを組んでみたので載せてみようかと。
前に書いた配列の動的確保より簡単で「C++らしい」コードが書けるコンテナというものを利用してみようと思います。
以下コード
// prime_num.cpp
#include <iostream> // cout,cinなどを使うするために必要
#include <math.h> // 平方根を取得するsqrt関数を使うために必要
#include <vector> // vectorコンテナを使うために必要
using namespace std;
/* こう書いておくことで例えばstd::cout << "hogehoge";を
cout << "hogehoge";に短縮できる(std::が省略できるようになる)*/
bool Prime(unsigned long int n); // 判別関数
int main(void) {
// 入力
unsigned long int n;
cout << "調べたい値:";
cin >> n;
// 入力された値を関数に渡し、返り値に合わせてメッセージを出力
cout << n << "は素数";
if(Prime(n)!=true)
cout << "でない";
cout << endl;
return 0;
}
//判別関数の実装
bool Prime(unsigned long int n){
/* 最小の素数だけ手動で格納
最小の素数は2であり、それ以降の素数は3以上であるためiの初期値は3
nが素数でない場合はnの平方根以下に約数が存在するので、
nの平方根以下のみを調べれば良い
2を除く素数はすべて奇数なのでiは2ずつ増加させる
素数の判別方法はエラトステネスの篩を調べてみると良いと思います*/
vector<int> Primes;
Primes.push_back(2);
for(int i=3; i<=sqrt(n); i+=2){
bool flg=true;
for(int j=0; j<Primes.size(); ++j){
if(i%Primes[j]==0) flg=false;
}
if(flg==true)
Primes.push_back(i);
}
bool flg=true;
for(int i=0; i<Primes.size(); ++i){
/* Primes[0]の2は例外的に格納しているため、
素数かどうかも例外的に判断する
nが2でなくてnの平方根以下の素数で割り切れる場合、nは素数でない*/
if(n%Primes[i]==0){
if(n!=2) flg=false;
break;
}
}
return flg;
}
入力されたnによって配列の必要な要素数が変わるため動的に処理してくれるvectorコンテナを利用します。
宣言:vector<型> オブジェクト名;
最後尾にデータを追加:オブジェクト名.push_back(追加したいデータ)
最後尾のデータを削除:オブジェクト名.pop_back(追加したいデータ)
要素数の取得:オブジェクト名.size()
先頭要素のポインタ取得:オブジェクト名.begin()
最終要素のポインタ取得:オブジェクト名.end()
通常の配列同様にオブジェクト名[要素番号]で要素へのアクセスが可能です。
また、オブジェクトのポインタからメンバにアクセスする際には
ポインタ名.メンバ、ではなく
ポインタ名->メンバでアクセスします。
(->はアロー演算子というものです。)
コンテナと一緒にお約束のように使われるイテレータについてはまた後日別記事にて書こうと思います。
今回はここまで。
2015/12/22 Tue.
【ジョーカースクリプト勉強会】キャラ画像編【第4回】
はい、ふくともです。
前回の内容は基礎的な部分終わってないのにやるのもどうなんだと思いつつ公開してしまいました。
またまた時間は空いてしまいましたが気を取り直して第4回、キャラ画像編!(とりあえずサンプルのゲーム画面にキャラ写ってたらそれっぽく宣伝始められるねやったね)
用意するもの(ファイル名は適当)
※キャラクター画像の背景は透過を済ませておくこと
Assets/JOKER_GAME/Resources/novel/data/images/character/
以下に前述の画像を配置
以下関連タグ(引数の値はサンプル)
- キャラクター定義:[chara_new name="CHAR_NAME" storage="CHAR_FACE0"]
- キャラクター削除:[chara_remove name="CHAR_NAME"]
- キャラクター表示:[chara_show name="CHAR_NAME"]
- キャラクター非表示:[chara_hide name="CHAR_NAME"]
- キャラクター移動:[chara_pos name="CHAR_NAME" x=N y=M]
- 表情登録:[chara_face name="CHAR_NAME" face="1"storage="CHARA_FACE1"]
- 表情切り替え:[chara_mod name="CHAR_NAME" face="1"]
基本的な流れとしては
- キャラクター定義と表情登録
- キャラクター表示
- 表情変更や表示位置移動
- キャラクター非表示
- キャラクター削除(メモリ解放、快適なプレイのため
[chara_new]の際に引数で tag="TAG" を与え、[chara_show tag="TAG"]・[chara_hide tag="TAG"]とすることで一括表示/非表示が可能
[chara_new]で選択した表情は[chara_mod name="CHAR_NAME" face="default"]で選択可能
[chara_show]、[chara_hide]の引数に time=N を与えることでN秒かけてフェードイン/フェードアウトさせることが可能
[chara_pos]同様に[chara_show]の引数に x=N y=M を与えることで水平方向にN、垂直方向にMの位置に表示させることが可能
一旦削除したキャラクターの仕様には[chara_new]で再定義が必要
その他の引数もあるけどとりあえず上記のものを把握しておけば支障はないはず。
http://jokerscript.jp/home/tag#chara_new ←に詳しく載っているので物足りなければこちらを。(画像の拡大率や名前欄との連携等もできます)
かなり雑で駆け足な解説でしたがキャラクタ画像に関しては以上。
(サンプルプロジェクト立ててスクショ見せつつ進めたほうが良かった気はしなくもなくも)
次回、音声編で一通りジョーカースクリプト勉強会は終わりになるかと思いますが、何か思うところがあれば随時延長戦は行うつもりです。
本筋とは若干ズレますが、先日ジョーカースクリプトの製作者様が改善作業をする、という旨のツイートをしていらっしゃいましたので、1ユーザーとして非常にありがたいなぁ、と。
それに伴って既出の記事も更新するかもしれませんので、よろしければ今後の記事と併せてチェックしていただけると幸いです。
2015/10/08 Thu.
【ジョーカースクリプト勉強会】揺らしエフェクト編【第3回】
お久しぶりです、ふくともです。
先日ブログ名を変更してみました。
何か意味があるわけではないのですが、以前のタイトルだとちょっと硬いかなぁ、と思ったのでキャッチーと言うかなんというか、ゆるーくしてみようかと思った次第です。
もし個人サークルで技術本作ったりするとしたらサークル名もこれにしましょうかね
友人に連絡中に予測変換でドラッグ・アンド・ドロップがドラッグ・アンド・ドロップキックになってしまって語感が気に入っただけで深い意味はこれっぽっちもありません(笑)
さて、久々の更新となったわけですが、自分が所属しているサークルがコミックマーケットに出展したわけでして、その締め切りに追われて更新する暇がなかったのです。
その間に学んだこと/覚えたことは色々あるものの、発信できていなかったのでその分をこれから発信できればなぁ、と思います。
......で、今回は前回少しだけお話した自作プラグインについて書かせていただきます。
モノ自体は非常にシンプルな揺らしエフェクトです。
作成した経緯ですが、
自サークルが作った作品は当初、吉里吉里2/KAG3を利用していたためこのような揺らしタグが標準搭載されていたわけですが、ジョーカースクリプトには非実装でした。
しかし、この揺らしがないとインパクトに欠けるから揺らさないわけにはいかない!!
ならば自作しよう(というか自作せざるを得ない)ということで作ってみたわけです。
......といってもジョーカースクリプトはテキストノベル作成のためのアセットなのでそれ単体では実装できない(or実装が難しい)と考えたため他の画面エフェクトができそうなライブラリを探し、ジョーカースクリプトにプラグインとして組み込むことに。
僕が持っている唯一のUnity資料
何かないかなぁ、と読み漁ってみると
iTweenなるものがあるらしい。(最初iTunesと読み間違えたのは秘密)
使い方はこちらのWikiを参考にするといいと思う。
日本語での解説とサンプルコードが掲載してあるので非常にわかりやすかった。
導入についてはwikiの方で十分解説してあったため割愛。
本の中では「ダメージエフェクトでキャラクターを揺らしたい」というシチュエーションで利用されている。
この対象を「キャラクター」ではなく「画面全体」にしたら実装できるのではないかと考えた。
現在位置からの移動
// このコードを適用したgameObjectを5秒掛けて(3, 3, 3)の位置まで移動させる iTween.MoveTo(gameObject, new Vector3(3, 3, 3), 5.0f);第一引数に移動させたいGameObjectを指定、
第二引数に移動させたい位置をVector3で指定、
第三引数に移動させる時間をfloatで指定します。
ハッシュテーブル
Hashtable table = new Hashtable(); // ハッシュテーブルを用意 private void Start() { table.Add("x", 10); // xを10まで移動 table.Add("y", 5); // yを5まで移動 table.Add("time", 3.0f); // トゥイーン時間は3秒 table.Add("delay", 1.0f); // 1秒遅らせてからトゥイーンスタート iTween.MoveTo(gameObject, table); // 第二引数にハッシュテーブルをセット }ハッシュテーブルを使用したトゥイーン方法です。
iTween.MoveTo(gameObject, iTween.Hash("x", 10, "y", 5, "time", 3.0f, "delay", 1.0f));iTween.Hash("プロパティ", 値, "プロパティ", 値, ...)
と続けて一行で書くことも出来ます。
揺らす
private void Start() { // 現在位置からx:-20までランダムに揺れる iTween.ShakePosition(gameObject, iTween.Hash("x", -20)); // 現在角度からy軸260度までランダムに回転する iTween.ShakeRotation(gameObject, iTween.Hash("y", 260)); // 現在スケールからz軸を10倍にしたサイズまでランダムに拡大縮小する iTween.ShakeScale(gameObject, iTween.Hash("z", 10)); }private void Start() { // Vector3.one * 10(Vector3(10, 10, 10))を指定しているので、xyz方向に大きく揺れる // "z", 1はおそらくVector3(0, 0, 1)なので、amountのVector3に効果が上書きされている模様 iTween.ShakeScale(gameObject, iTween.Hash("z", 1, "amount", Vector3.one * 10)); }amountで揺れる大きさをVector3オブジェクトで指定します。
GameObject.Find
まずは、オブジェクトの「名前」からアクセスをする方法です。
どうやらオブジェクト名はここから分かりそうだ。
自分の環境を見てみると、
この「MainCamera」 というのが「画面」に当たりそうである。(そして実際にこれで合っていた)
オブジェクト名はわかったものの、この先どう扱えばいいのか
とりあえずゲームオブジェクトに関する資料をもう少し探す。
すると、いかにもなページを発見。
Find()でゲームオブジェクトを取得しよう
Find()を用いることで"ゲームオブジェクト名"から
ゲームオブジェクトを取得できます。適当なPlaneを作ったとします。
//適当なGameObject型の変数 gを作成 public GameObject g; void Start () { //g に最初に見つかった名前が Plane のゲームオブジェクトを取得する g = GameObject.Find("Plane"); print ( g ); print ( g.audio); //出力 Plane //出力 null }
なるほど。
ということは、
GameObject.Find("MainCamera");
で画面のオブジェクトを取得できるわけだ。
ここまで分かれば何となく行けそうな気がする。
プラグイン作成に関しては前に書いた記事とジョーカースクリプト公式のプラグイン作成解説ページを参照。
以下コード。
QuakePluginComponent.cs
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace Novel{
public class QuakeComponent:AbstractComponent{
public QuakeComponent(){}
GameObject target = GameObject.Find("MainCamera");
public override void start(){
iTween.ShakePosition(target, iTween.Hash("x",1.5, "y",1.5, "time",5.0f));
this.gameManager.nextOrder();
}
}
}
これで縦横に画面が5.0秒間揺れる[quake]タグが作れました。
ダウンロードはGitHubからどうぞ。
揺れ幅や時間が固定だから、タグ呼び出し時に値を指定できるようにするともっと使い勝手がいいのかなぁ、と思いつつ実装方を考える余裕が時間/精神どちらも無かったから断念した。
これを叩き台に誰かがいじってくれたらとても嬉しい(自分でやれって話ではあるけども。)
とりあえず今回はここまで。
少し話は戻りますが、私が所属しているサークルを少し紹介させていただきます。
サークル名:outer(base);
活動ジャンル:ノベルゲーム制作(オリジナル)
メンバー
HP:http://outer-base.mydns.jp/【ジョーカースクリプト勉強会】背景編【第2回】
また更新の間が開いてしまいました、ふくともです。
キャラクタ画像はまた別の回で解説させていただきます。
今回は背景について。
以下関連タグ
- 背景作成:[bg_new storage="BACKGROUND_IMAGE"]
- 背景切替:[bg_mod storage="BACKGROUND_IMAGE" time=N]
BACKGROUND_IMAGEは使用する画像ファイルで、プロジェクト内の
Assets/JOKER_GAME/Resources/novel/data/images/background/
ディレクトリから選択(このディレクトリまでのパスは記述しない)、拡張子は省略可能です。
time=NはN秒かけて切替えます。
- 背景削除:[bg_remove]
- 背景表示:[bg_show]
- 背景非表示:[bg_hide]
基本的な流れとしては
[bg_new storage="BACKGROUND_IMAGE1"]
[bg_show]
〜テキスト等を記述〜
[bg_mod storage="BACKGROUND_IMAGE2"]
〜テキスト等を記述〜
[bg_mod storage="BACKGROUND_IMAGE3"]
と記述していき背景が不要になったところで
〜テキスト等を記述〜
[bg_hide]
[bg_remove]
と削除してあげれば良いです。
次回は画面全体を対象とするエフェクトプラグイン(自作)について書きたかったのでキャラクタ画像や音声より先にこちらを記事にしました。
自分としては割と便利なプラグインだと思うので期待していただけると幸いです。
2015/07/11 Sun.