【ジョーカースクリプト勉強会】揺らしエフェクト編【第3回】

お久しぶりです、ふくともです。

先日ブログ名を変更してみました。

 

何か意味があるわけではないのですが、以前のタイトルだとちょっと硬いかなぁ、と思ったのでキャッチーと言うかなんというか、ゆるーくしてみようかと思った次第です。

 

もし個人サークルで技術本作ったりするとしたらサークル名もこれにしましょうかね

 

友人に連絡中に予測変換でドラッグ・アンド・ドロップがドラッグ・アンド・ドロップキックになってしまって語感が気に入っただけで深い意味はこれっぽっちもありません(笑)

 

さて、久々の更新となったわけですが、自分が所属しているサークルがコミックマーケットに出展したわけでして、その締め切りに追われて更新する暇がなかったのです。

その間に学んだこと/覚えたことは色々あるものの、発信できていなかったのでその分をこれから発信できればなぁ、と思います。

 

改良版の記事はこちら

 

......で、今回は前回少しだけお話した自作プラグインについて書かせていただきます。

 

 モノ自体は非常にシンプルな揺らしエフェクトです。

作成した経緯ですが、

自サークルが作った作品は当初、吉里吉里2/KAG3を利用していたためこのような揺らしタグが標準搭載されていたわけですが、ジョーカースクリプトには非実装でした。

 

しかし、この揺らしがないとインパクトに欠けるから揺らさないわけにはいかない!!

ならば自作しよう(というか自作せざるを得ない)ということで作ってみたわけです。

 

......といってもジョーカースクリプトはテキストノベル作成のためのアセットなのでそれ単体では実装できない(or実装が難しい)と考えたため他の画面エフェクトができそうなライブラリを探し、ジョーカースクリプトプラグインとして組み込むことに。

 

僕が持っている唯一のUnity資料

www.amazon.co.jp

何かないかなぁ、と読み漁ってみると

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オブジェクトで指定します。
 
wikiによると、この第一引数のgameObjectがエフェクトの対象らしい。
ということは、ここに「画面」を指定すればよいのではなかろうか。
 
そのためには「画面」にあたるGameObject名を取得しなければならない。

 

albatrus.com

GameObject.Find

まずは、オブジェクトの「名前」からアクセスをする方法です。

2014-6-19-fdsasa

 どうやらオブジェクト名はここから分かりそうだ。

 

自分の環境を見てみると、

f:id:q-fukutomo:20150818184638j:plain

この「MainCamera」 というのが「画面」に当たりそうである。(そして実際にこれで合っていた)

 

オブジェクト名はわかったものの、この先どう扱えばいいのか

とりあえずゲームオブジェクトに関する資料をもう少し探す。

 

すると、いかにもなページを発見。

qiita.com

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からどうぞ。

github.com

 

揺れ幅や時間が固定だから、タグ呼び出し時に値を指定できるようにするともっと使い勝手がいいのかなぁ、と思いつつ実装方を考える余裕が時間/精神どちらも無かったから断念した。

 

これを叩き台に誰かがいじってくれたらとても嬉しい(自分でやれって話ではあるけども。)

とりあえず今回はここまで。

 

少し話は戻りますが、私が所属しているサークルを少し紹介させていただきます。

サークル名: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.

【ジョーカースクリプト勉強会】ゲーム終了編【第1.5回】

ご無沙汰してます、旧:ふくともです。

試験間近であまり余裕が無いので簡単に済まさせていただきます。

 

現在利用させて頂いているジョーカースクリプトですが、Unityのアセットのひとつでありゲームエンジンそのものとは異なるためゲームの起動や終了機能は組み込まれていません。

 

起動に関してはUnityからジョーカースクリプトが呼ばれるため問題無いですが、逆に終了する際にはジョーカースクリプト側からUnityに閉じるように指示を出さなければいけません。

 

なので、それを実行する最もシンプル(と思われる)終了タグを実装するプラグインを組んでみました。

 

このタグに到達したらそこでアプリケーションを終了させるだけで、確認ダイアログや自動セーブ機能などは何もありません。

叩き台程度に考えていただけるとよろしいかと思います。

 

以下コード

 

// [exit]でゲームを終了させるプラグイン

 

using UnityEngine;

using System.Collections;

using System.Collections.Generic;

 

namespace Novel{

    public class ExitComponent:AbstractComponent{

        public ExitComponent( ){ }

        public override void start( ){

            Application.Quit( );

        }

    }

}

 

このコードを

Assets/JOKER_GAME/Plugins/PluginComponent.csの中に貼り付けて

シナリオファイルの中で[exit]または@exitと記述していただければそこでゲームが終了するようになります。

 

このコードではマズい、機能追加してみた、などございましたら連絡いただけると幸いです。

2015/07/02 Thu.

 

追記

Assets/JOKER_GAME/Plugins/PluginComponent.csにコードを貼り付けずに

Assets/JOKER_GAME/Plugins/ExitPluginComponent.csというファイルを作成し、その中にこのコードを記述しても問題なく動くようです。

 

どのファイルがどのプラグインのものなのかわかるように、ファイルを分割したほうがメンテナンス性も上がっていいかもしれません。

2015/07/06 Mon.

【ジョーカースクリプト勉強会】テキスト編【第1回】

KAGスクリプトを使ったことがあると非常にやりやすいですね

導入法については公式さんを見ていただいたほうが早いかと。

jokerscript.jp

 

  • 表示テキスト(シナリオの文章そのもの、デフォルトで1行当たり全角31字まで)
  • ラベル(シナリオの繋ぎに関わるもの、内容自体は非常に簡単)
  • タグ(クリックを待たせる、改行を行う、別のシナリオへ飛ぶ、などといった処理の部分、コレが肝)

 の3つを押さえれば作れると思います(暴論)

画像や音声とかのリソース系のタグについてはまた別記事で書こうと思います。

 

ラベルの表記法

*LABEL

ラベル名の前に*を付けるだけ。

この例だとLABELというラベルが出来ます。

ラベルについてはこれだけ。

 

 

タグの表記方

  1. [TAG]
  2. @TAG

ex.) [l]と@lは同じ挙動をする

注意. @TAGで書く場合は一行で収める必要あり

例えば

  • [wait
    time=3]
  • @wait
    time=3

前者はOK、後者はNGということ

 

主要タグ一覧

  • クリック待ち:[l]
  • 改行:[r]
  • 改ページ(クリック待ち含む):[p]
    ※[l][p]と記述すると改ページまでに二回クリックが必要になります。
  • N秒待ち:[wait time=N]
  • 別のシナリオへ飛ぶ:[jump file="SCENARIOFILE" target=LABEL] 

 

jumpタグの引数について

引数が色々あって他のタグに比べやや複雑なので解説を。

  • file=SCENARIOFILE:SCENARIOFILEというファイルに飛ぶ、シナリオファイルを分割する際に有用。
  • wide(またはtall)ディレクトリから指定する。
    指定しなければ現在のファイルと同じファイルを指定したことになる。
  • target=LABEL:指定ファイル内のLABELというラベルに飛ぶ指定しなければそのファイルの先頭に飛ぶ。
  • scene=new:new を指定すると新しくシーンを作成した上でジャンプします。
    ※シーンとは:表示している背景やキャラ情報などのこと、これを新規作成すると表示情報を一旦クリアできる。
  • index:正直自分もよくわからない
    こちらで質問中

たとえば

  1. 「サンプルテキスト」と表示して
  2. クリック待ち
  3. SCENARIO2のLABEL2に飛んで
  4. 「jump後」と表示

ということをしたい場合

 

SCENARIO1.txt

【前略】

サンプルテキスト[l]

@jump file="wide/SCENARIO2" target=*LABEL2

 

SCENARIO2.txt

*LABEL2

jump後[l]

【後略】

 とすればOKというわけです

 

これだけ覚えれば絵/音/エフェクト/分岐/タイトル画面無しの簡易ノベルゲーム(と呼べるかも怪しい物)が組めますね

 

これだけでは流石に寂しいのでまた次回は今回触れなかった辺りについて書いてみようと思います。

 

「間違ってるよ!!」とか「うまく動かねぇぞ(# ゚Д゚)ゴルァ!!」ということであればその旨のコメントをいただけると嬉しいです。

 

2015/05/29

 

【C++】stringの関数による書き換えについて

ふくともです。

C++でstringに格納した特定の文字だけを変換する、という処理を行おうとしたところコケたのでメモ的に書いておく

 

〜イメージ〜

hogehoge → 変換関数 →h0geh0ge

 

コード

#include <iostream>

#include <string>

using namespace std;

void leet(string, int);    // 変換関数

int main(){
    string str="hogehoge";
    leet(str, (int)str.size());
    cout << str << endl;
    return 0;
}

// 変換関数

void leet(string str, int size){

    for(int i=0;i<size;++i)

        if(str[i]=='o') str[i]='0';

    cout << str << endl;

    return;

};

 

コード自体はすごく簡単

stringオブジェクトと、その文字数(サイズ)を関数に渡す

→先頭から一文字ずつ調べて o があれば 0 に置き換える

 

で、実行結果......

hogehoge

......あれ?

変わってない

 

試験的に関数内部でも出力

#include <iostream>

#include <string>

using namespace std;

void leet(string, int);    // 変換関数

int main(){
    string str="hogehoge";
    leet(str, (int)str.size());
    cout << "main() : " << str << endl;
    return 0;
}

// 変換関数

void leet(string str, int size){

    for(int i=0;i<size;++i)

        if(str[i]=='o') str[i]='0';

    cout << "leet() : " << str << endl;

    return;

};

 

実行結果

leet() : h0geh0ge

main() : hogehoge

 

......書き換えはできてないけど、オブジェクトのコピーはされてるみたい

↓詳しくはここのローカルコピーについて読むといいかと。

qiita.com

 

ローカルコピーさせない、ってやり方もないことはないのかもしれないけどめんどくさそう結構な手間が掛かりそうだから別の方法を。

 

よくよく考えてみると、関数によってコピーされてるってことは関数が終了したらスコープの関係でコピー分のメモリは開放されるから元のオブジェクト直接書き換えなくてもいいよね

 

というわけで、書き換え後のstringオブジェクトを返して、それを元のオブジェクトに上書きしてみる

 

#include <iostream>

#include <string>

using namespace std;

string leet(string, int);    // 変換関数

int main(){
    string str="hogehoge";
    str = leet(str, (int)str.size());
    cout << "main() : " << str << endl;
    return 0;
}

// 変換関数

string leet(string str, int size){

    for(int i=0;i<size;++i)

        if(str[i]=='o') str[i]='0';

    cout << "leet() : " << str << endl;

    return str;

};

 

実行結果

leet() : h0geh0ge

main() : h0geh0ge

 

 

目標達成。

最後に試験的に書いてて必要なくなった部分をコメントアウトor削除したら終了。

<iframe>内コンテンツのサイズ変更

お久しぶりです、旧:ふくともです

先日webサイトに別のページを取り込み、その取り込んだページのサイズを指定サイズに変更したいなぁ、と思ったところ割と手こずったので書き出しておこうと思った次第。

※CSS3でないとうまく行きません。

HTML

<div class="parent">

  <div class="child">

    <iframe id="external" src="SITE URL" scrolling="no"></iframe>

  </div>

</div>

CSS

#external{

  width:578px;

  height:771px;

  transform:scale(0.65); transform-origin:0 0;

  -o-transform:scale(0.65);-o-transform-origin:0 0;

  -webkit-transform:scale(0.65); -webkit-transform-origin:0 0;

  -moz-transform:scale(0.65); -moz-transform-origin:0 0;

  -ms-transform:scale(0.65); -ms-transform-origin:0 0;"

}

.parent{

  width:375px;

  height:400px;

  overflow-x:hidden;

}

.child{

  width:375px;

  height:450px;

  overflow:hidden;

}

 

〜解説〜

#external

<iframe>のサイズは内部コンテンツと同じに設定(私が使ったものは578×771だったのでこの値、適宜変更してください)

構造上、上の階層の<div>からはみ出してしまうのでscrolling="no"

transform:scale(N);は縦横比を維持し内部コンテンツをN倍にするので、横幅を表示したい領域(後述のparent)に合わせて375/578≒0.65

transform-origin:0 0;は縮小の起点座標ををコンテンツの左上に設定する(デフォルトではコンテンツの中心)

 

.parent

表示したいサイズを設定

横幅がはみ出してしまうためoverflow-x:hidden;を指定

 

.child

横幅に合わせたコンテンツのサイズを設定

縦幅がはみ出すのでoverflow:hidden;を指定

横幅はぴったり合わせてあるのでoverflowにしたが、明示的にoverflow-yでも問題ない。

 

transformの仕様上、コンテンツの上下左右に余計なマージンが発生するので、コンテンツサイズに合わせた<div class="child">を<div class="parent">上でスクロールさせて実装している。

多次元配列の動的確保

やや、どーもどーも、ふくともです。

今回は宣言通り多次元配列の動的確保についてまとめますです。

 

・そもそも多次元配列って何ぞ?

正直うまい例が思いつかないんだけど、

複数の軸を持つ座標ってところかな

一次元配列はX座標だけで

二次元配列はX座標、Y座標で

三次元配列はX座標、Y座標、Z座標で、という風に表現されるの。

二次元配列ならint array[y][x];

三次元配列ならint aray[z][y][x];

って感じで、型 配列名の後に各次元の要素数を書いていく。

 

(使用可能な)全体の要素数は各次元の要素数の積になってて

int array[3][3][3];

って宣言すると3*3*3で全部で27要素で

array[0][0][0] array[0][0][1] array[0][0][2]

array[0][1][1] array[0][1][1] array[0][1][2]

array[0][2][0] array[0][2][1] array[0][2][2]

array[1][0][0] array[1][0][1] array[1][0][2]

array[1][1][1] array[1][1][1] array[1][1][2]

array[1][2][0] array[1][2][1] array[1][2][2]

array[2][0][0] array[2][0][1] array[2][0][2]

array[2][1][1] array[2][1][1] array[2][1][2]

array[2][2][0] array[2][2][1] array[2][2][2]

が使用可能

 

配列の掛け算になってる、くらいの理解でだいたいOK

素数は各次元で違っても問題なくて int array[3][5]; みたいな宣言も問題無いよ

 

で、その多次元配列(説明を簡略化するために二次元配列)の動的確保は前回を元にすると

int *array = new int[y][x];

と書きたくなるんだけどこれは間違い。

 

多次元配列は配列の配列なんです。

前回の記事を参照してもらえれば分かるかと思いますが、int * はint型配列を示すから配列の配列は示せないのです。

 

じゃあ、どうするかというとint  *が int型配列を示すのでint型配列の配列は

int **array = new int[y][x];

 

......惜しい。

newは指定された要素数配列を確保するためのものだからこれも間違い

でも配列の配列もint型の配列の配列 (だんだんわけがわからなくなってきたかな?)

「int型の配列」型の配列として考えると

int **array = new int*[y];

for(int i=0; i<x; ++I)

    *array[i] = new int[x];

と書くことで解決できる。

 

これは

「int型の配列」型の配列を動的に確保する

→その要素(つまりint型の配列)をそれぞれ動的に確保する

という仕組みになっているわけです。

 

三次元配列なら

int ***array = new int**[z];

for(int i=0; i<y; ++i){

    **array[i] = new int*[y];

    for(int j=0; j<x; ++j)

        *array[i][j]  = new int[x];

}

と入れ子にしてあげれば大丈夫。

 

delete[ ]は内側(とここでは表現する。 宣言時の右側の次元)から順に行う

for(int i=0; i<x; ++i)

    delete[ ] array[i];

delete[ ] array;

三次元以上の配列も同様に、適宜delete[ ]を入れ子にしてあげてくださいな。

 

前に書こうって言ってから一週間くらい経ってしまったけど一応こんな感じです。

前回致命的な記述ミスがあったことをもう一度お詫びします、申し訳ございませんでした。

今回も何かミス等があれば報告していただけると助かります。

報告は記事へのコメントでも構いませんし、下記のいずれかでも大丈夫です。

http://ask.fm/kata_tsuki

https://twitter.com/kata_tsuki

katatsuki52☆gmail.com (☆は@(半角)に置き換えてください)

2014/12/20 Sat.