【ジョーカースクリプト勉強会】ゲーム終了編【第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.

配列の初期化(+配列の動的確保)について

どーもどーも、久々の更新です。

今回はJOI予選中にハマってしまったことについて書かせていただこうかと。

 

問題文は省略。

回答作成中にint配列の全要素を-1で初期化したかったから

int array[n] = {-1};

と書いたわけですが、これが間違い。

n==5;

とすると

イメージ

array[0] == -1;

array[1] == -1;

array[2] == -1;

array[3] == -1;

array[4] == -1;

現実

array[0] == -1;

array[1] == 0;

array[2] == 0;

array[3] == 0;

array[4] == 0;

ってなるらしい。

 

それを知らなかったために色々と余計な部分を確認した。

入力データも処理も確認したけどおかしくはない、となると他の部分?

と思い変数の値をすべて出力するという暴挙に。(デバッグツールっておいしいの?)

その結果、

array[0] == -1;

array[1] == 0;

array[2] == 0;

array[3] == 0;

array[4] == 0;

......えー、何々どゆこと?

これは入力データに依存しないはずだぞ?

代入(初期化)ミス?

 

という訳で検索した結果。

初期化子リストの値の数が集約型より少ない場合、集約型の残りのメンバーまたは要素は 0 に初期化されます。

 

お、おぅふ......。

いやぁ、これ知らなかったせいでループ内の処理とか入力データを延々と睨み続けるハメに......。

普段一律の値で配列初期化するときは0ばっかりで

int array[n]={0};

って書いてて、てっきり値を1つしか書かなければその値が全部代入されるのかと思ってたんです(汗)

 

なのでここのコードを

int array[n];

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

    array[1]=−1;

と書き換えた結果、狙い通り動いてくれた(全要素に代入してるんだから当たり前)

 

あ、あと配列ついでにもう一個、いわゆる配列の動的確保。

動的確保ってなんぞ?って人のために(雑に)説明すると、

・配列使いたい!!

・要素数は入力された値使いたい!!→コンパイル時には要素数未定、実行時に確定させたい!!

って時に使う技術。

(実際には入力以外の要因で使いたい場面もいっぱいあるだろうけど割愛、そういう場面でも使えるから安心してね!!)

 

じゃあ、ソース書くぜ!!

// array-sample1.cpp

#include <iostream>

using namespace std;

 

int main(){

    int n;

    cin >> n;

    int array[n];

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

        array[i]=0;

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

        cout << "array[" << i << "] ==" << array[i] << endl;

    return 0;

}

と書いたら(開発環境にも依るっぽいけど)コンパイルエラー、または実行時にエラーが出る。

環境によって動作の変わるプログラム→不安定のプログラム、を組むということでよろしくない。

なのでここのコードを

int array[n];

から

int *array = new int[n];

と書き換えちゃいます!!

int *arrayint型の配列を「示す」よ〜(配列そのものじゃないよ!!)ってやつで、

そのあとのnew int[n];int型の領域をn個確保(つまり動的確保!!)を代入するよ!!ってやつ。

......え?動的確保ってことは結局未定のまんまじゃん、って思うだろうけどそれでいいの。

newは動的確保を可能にするためのものだからこれでいいんです!!

 

で、注意としてはnewで確保した領域はdeleteで解放しなきゃいけないっていうのがあって、それの書き方は

delete array[ ];

これだけです笑

newの時と違ってarrayのサイズが決まってるから要素数は書かなくていいの。

というわけでさっきのコードを修正すると

// array-sample2.cpp

#include <iostream>

using namespace std;

 

int main(){

    int n;

    cin >> n;

    int *array = new int[n];

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

        array[i]=0;

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

        cout << "array[" << i << "] ==" << array[i] << endl;

    delete[ ] array;

    return 0;

}

これでOK

 

......なんか初期化より動的確保がメインになっちゃったかも(汗)

取り敢えずここまで!!

もっと詳しく知りたいって人はここ見るといいかも→C++編(言語解説) 第12章 new/delete

次回は多次元配列の動的確保について書いてみようかな

 

2014/12/14 Sun.

 

2014/12/18 Thu.

一部修正。

new[ ]で確保した領域はdeleteではなくdelete[ ]で開放しなければなりませんでした。

私のせいで勘違いしてしまった方がいらっしゃいましたら申し訳ありませんでした。

Windows10TechnicalPreview

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


Windows Technical Preview - Windows Insider Program - Microsoft Windows

 

β版と聞けば気になっちゃうよね

というわけでインストールしてみた。

(と言っても機体はMBA Mid 2012だから純正じゃなくてVirtualBoxなんだけど。)

 

取り敢えずOSイメージください、というわけでDLしに行くと......

f:id:q-fukutomo:20141202193041p:plain

そ、想定内だもんね!!

イメージさえ有れば俺の勝ちですし......(震え声)

Windows Technical Preview ISO のダウンロード - Microsoft Windows

f:id:q-fukutomo:20141202193618p:plain

......え?

\ページの言語は日本語あるのに日本語OSは無い!!/

 

仕方なく英語版の64bitを導入することに。

設定は以下の通り。

f:id:q-fukutomo:20141202194432p:plain

一番の注意点はOSの種類をWindows8.1にすることかな。

その他はDirectXを導入する予定だからビデオメモリを最大にしたことと、ある程度動作に余裕を持たせるためプロセッサを4つ(使用率制限100%)、メインメモリを4GBに設定した、くらい。

ネットワーク設定はVirtualBoxの基本(らしい)のNATに設定し、ホストOSと同じものを利用する。

仮想ハードディスクのサイズは任意で。

俺は取り敢えず100GBにしておいた。

 

いざ起動。

最初はOSイメージ(今回はWindowsTechnicalPreview-x64-EN-US)から起動して仮想ハードディスクにインストール。

 

アカウント設定については省略。

 

OSのインストールが終わったら一旦再起動し、VirtualBoxのお約束Guest Additionsのインストール。

何のためのどういうものなのかについてはこちらを参照

→ Guest Additionsのインストール | VirtualBox Mania

※前述のDirectXはここでのDirect3Dのこと

 

基本的にはこれでOK

あとはアプリケーションインストールしたり、壁紙とかの外観変えたりお好きな様に。

 

取り敢えずこれでも自称エンジニア(見習い)なので開発環境は整える。

Windowsを最新のにしたついでに環境も最新のにしてみる。

Microsoft Visual Studio Express 2013 for Windows Desktop をインストール!!

 

......実はこれのインストールに時間かかりすぎて暇だったからこの記事書いてます。

というわけで今回はここまで!!(雑)

環境構築まで気になる方はこちらを参考にするとよろしいかと。

Windows 8.1 で作る 無料の VC++ 開発環境 - Planetleaf.com Lab.

Windows8.1ってなってるけど、気にしなくて大丈夫。

 

次回は環境整え終わってからそれでも晒そうかなぁ......

2014/12/02 Tue.