変数の型・演算子・定数(前処理指令)
int型以外にも変数の型は存在します。
演算子は"+,-,*,/"以外にも沢山あります。
ここでは変数の型と演算子のはたらきを詳しく見てみましょう。
また、定数の使用方法も紹介します。
変数の型
整数を扱う変数を宣言する方法は、
int 変数名;
でしたね。
一般的に変数を宣言するときには
変数の型 変数名;
と記述します。
さて、intとは「整数を扱う」と言うことを指示していると説明しました。
では整数以外を扱う変数があるのでしょうか?
実は存在します。(正確に言うと、ACSで扱う変数はすべて整数を扱いますが、それは後ほど説明します。)
zdoomがサポートしている変数の型は次の様な物があります。
int型
(整数)
str型
(文字列)
bool型
(ブール型)
これらの変数の型を詳しく見ていきましょう。
int型
int型は最も使用回数の多い変数の型です。
int型をマスターしましょう。
int型には様々な使い方があります。
これまで紹介してきた変数はすべてこれにあたります。
int a = 4;
などがこれにあたります。
特に説明することもないので、
int型整数のすべてに共通する「ビット」について詳しく見てみましょう。
zdoomは、int型整数(int型整数でなくても)を使用するとき、10進数ではなく、2進数(ビット)で理解しています。
つまりどういうことかというと、
5
という値(10進数)は
101
という値(2進数)で理解しています。
zdoomでは、整数を32桁の2進数(32個のbit)を扱ってすべて整数を理解しています。
つまり、それぞれのint型変数(整数も)は
00000000000000000000000000000000
から
11111111111111111111111111111111
までの値を表現することができます。(2^32通りですね。)
ここで、左側のbitを上位ビット、右側のビットを下位ビットと呼びます。
この32bitをそのまま2進数として受け取って10進数に変換することもできますが、
負の数を扱えなくなるので、一番左側のビットを正負の記号に割り当てます。
表にして見てみましょう。(下に行くほど1値が増やしています。)
2進数 | 10進数 |
00000000000000000000000000000000 | 0 |
00000000000000000000000000000001 | 1 |
00000000000000000000000000000010 | 2 |
・・・ | ・・・ |
01111111111111111111111111111111 | 2147483647 |
10000000000000000000000000000000 | -2147483648 |
10000000000000000000000000000001 | -2147483647 |
10000000000000000000000000000010 | -2147483646 |
・・・ | ・・・ |
11111111111111111111111111111111 | -1 |
00000000000000000000000000000000 | 0 |
・・・ | ・・・ |
↑これを2の補数表現と呼びます。詳しくはあまり知りませんが、非常に便利な表現方法です。
なんとなく覚えやすい法則があることがわかりますね。
表を見てわかるように、int型変数は
-2147483648〜2147483647までの値を扱うことができます。
ACSを作るときは、すべての値がこれに収まるようにしましょう。
この値があふれたらどうなるのかと思われると思いますが、ちゃんと対策がとられています。
表を見てわかるように、整数はループしている(最大の整数と最小の整数が繋がっている)ことがわかります。
つまり、2147483647に1を足すと-2147483648になりますし、
-2147483648から1を引くと2147483647になります。(そのほかの値の和、差、積なども同様です。)
大きな数字を扱うときには値があふれないかどうか考えるようにしましょう。
さて、さきほどのビットの並びを見てint型を「数値ではなくビットの並びとして解釈する」ということもできると推測することができますね。
どういう事かというと、
「ON、OFFなどの2種類の設定しか持たない設定を一つのint型変数に最大32個まで記録させることができる。」
ということです。
「一つの変数にはON、OFFを制御できる32個のスイッチがある。」
といった感じに似ています。
このようなint型のとらえ方を既存のACSの関数(数学的な関数や、ゲームの進行に影響を与えるコマンドみたいなものも関数です。printも関数です。)でしばしば使用するので、覚えていて損は無いと思います。
(HudMessgae関数などでこの考え方は使われています。)
ここで、ある疑問が浮かぶと思います。設定のON、OFFはどのように行われているのでしょうか。
それはこの章の後半のビット演算の部分で紹介します。
文字としてのint型はACSではあまり実用的でないように思いますが、紹介します。
int型変数は、文字(のコード)を格納する働きを持っています。
文字の格納方法は簡単です。次の例を見てみましょう。
int ch = 'c';
' '(シングルクオーテーションマーク)で文字を囲むと、その文字に対応する文字コードの値(整数値)が変数に格納されます。
シングルクオーテーションマークで囲める文字は一文字だけです。当たり前ですが、日本語はNGです。
格納した文字を画面に表示するためには
print(c:ch);
のように、出力指定「c」を使います。
ちなみに、これを「d」にすると、文字コードの値が表示されます。
'\n'や'\0'のように特殊な文字も存在しますが、それらについては画面に文字を出力する関数で紹介します。
zdoomは実数を固定小数点数(Fixed point numbers)という形で(擬似的に)扱うことができます。
さきほど"整数としてのint型"でビットについて紹介しましたが、それらを理解した上で固定小数点数について詳しく見ていきましょう。
固定小数点数の宣言方法は簡単で、私達が日常生活で使用する小数のように記述するだけです。
ただし、値が整数であっても小数部分に0を記述して下さい。
具体例を見てみましょう。
0.5
3.14
2.72
3.0
//整数でも.0を付ける
また、固定小数点数を扱う変数の宣言方法は
int (変数名);
実数を扱うのにint型(integer)なのは少し疑問ですが、理由は後ほどわかると思います。
初期化したいときは
int (変数名) = (実数);
のように記述するだけです。
固定小数点数を利用したACSを作ってみましょう。
#include "zcommon.acs"
Script 1 ENTER
{
int FixedVariable = 0.5;
//変数FixedVariableを宣言し、0.5で初期化。
print(f:FixedVariable);
}
と表示されます。
上記の例のように、print文で固定小数点数を出力するときはf指定子を使用します。
次に固定小数点数の仕組みを詳しく見てみましょう。
"整数としてのint型"でも紹介したようにzdoomではint型変数は32bitの大きさを持っています。
ここで、上位ビットの16bitを整数部分に、下位ビットの16bitを小数部分に割り当てた物が固定初数点数です。
図にすると
-整数部分- | -小数部分- |
0000000000000000 | 0000000000000000 |
といった感じになります。
例えば、1.5は
-整数部分- | -小数部分- |
0000000000000001 | 1000000000000000 |
となります。
0.25は
-整数部分- | -小数部分- |
0000000000000000 | 0100000000000000 |
といった感じになります。
このように、固定小数点数では2のべき乗の和で表せることのできる値しか表現できません。
例えば、先ほどのACSの変数FixedVariableの初期化文の値を1.3にしてみて下さい。
と表示されます。このように固定小数点数で実数を扱うときには注意が必要です。
ちなみに、固定小数点数で表すことのできない値は固定小数点数で表すことのできる最も近い値に切り捨てられます。
(さらに有効数字6桁で画面に表示されます。ただし、1.00000のような場合は1と表示されます。)
整数としてのint型と固定小数点数としてのint型は同じ"int型"なので、固定小数点数としてのint型を整数としてのint型として解釈することもできます。
(その逆も可能です。)
例えば固定小数点数1.5はビットの並び方から
1 * 2^16 + 1 * 2^15 = 1.5 * 2^16 = 98304
と解釈ができます。
つまり、固定小数点数が正の値の時
(固定小数点数) = (整数) * 2^16
(整数) = (固定小数点数) / 2^16
という関係が成り立ちます。
次の様なACSを見てましょう。
#include "zcommon.acs"
Script 1 ENTER
{
print(d: 1.0 );
//d出力であることに注意。
}
と表示されます。(すなわち2の16乗)
この例からも先ほどの関係が成り立つことがわかります。
ずっと前の解説で'*','/'演算子を紹介しましたが、これは整数のための演算子であって、固定小数点数で計算するためのものではありません。
FixedMul関数やFixedDiv関数を使用しましょう。
例えば
FixedMul( 3.0 , 6.0 );
と入力すると
18.0
という答えを返してくれます。
注意:
ZDOOMが表現できる値を超えると値がループすることを先ほど説明しましたが、0.0に近すぎる値で割り算を行うなどあまりにも大きな値を計算させるとZDOOMがクラッシュします。
bool型
bool型はほとんどACSで見かけられませんが、zdoomではbool型という型が用意されています。
一緒に学んでいきましょう。
bool型変数は値が真(TRUE)か偽(FALSE)かという二つの情報しか入れることのできない型です。
しかし、二つの情報しか入れることができないので非常に使いやすく、便利な型です。
実際に使ってみましょう。
#include "zcommon.acs"
Script 1 ENTER
{
bool vba;
vba = 3 == 3;
//変数vbaに真(TRUE)を代入する。
if( vba ){
print(s:"true");
}
else{
print(s:"false");
}
}
と表示されます。
最初の赤い字の部分を見てみましょう。
bool vba;
でbool型変数vbaを宣言します。
vba = 3 == 3;
ここでは、"3(左辺)と3(右辺)の値が等しいか"という比較の結果の値(前の前の章で学んだ"=="です。)を変数vbaに代入しています。
もちろん、実際のところはvbaに1が代入されます。("=="の左右の値が等しければ1を返します。)
if( vba )
この部分はすなわち
if( 3 == 3 )
と読み替えることができます。そのため、画面にTRUEと表示されました。
このように、bool型変数の役割は、その変数にちなむある命題があって、それが真(TRUE)か偽(FALSE)かを格納することだと思います。
しかし、実はbool型変数もint型変数と同じ能力を持っており、-2147483648〜2147483647までの値を扱えますが、
重要なのは0でないか(真)0であるか(偽)の違いです。これをしっかり理解しておきましょう。
(もちろん、bool型で四則演算を行うこともできますが、四則演算のための型ではないので真偽を扱う変数として使いましょう。
ちなみに、このような演算を論理演算と言います。)
ところが、bool型をint型で代用する人が多く、実際のACSではbool型をほとんど見かけません。しかし、論理演算を理解することは重要なことなので、bool型もついでに覚えて下さい。
(先ほどの例のboolの部分をintに書き換えても正常にACSは動きますが、本来の使い方ではありません。)
str型
str型はint型と同様に非常に重要な型です。
int型よりも少し難しいですが、意外と原理は単純なので、ここでは原理を覚えましょう。
ループ文を扱うとき、str型変数の(実用的な)使い方を紹介します。(その9)
文字列型(str型)とは、文字通り「文字の集まり」を扱う型です。
前までの例でも出ていましたように、""(ダブルクオーテーションマーク)で囲まれた部分が文字列です。
(例えば"This is my string"などが文字列に当たります。)
では、実際に文字列を使ってみましょう。
#include "zcommon.acs"
Script 1 ENTER
{
str mystring = "This is my string!";
print(s:mystring);
}
と表示されます。
最初の赤い字の部分を見てみましょう。
str mystring = "This is my string!";
str mystring
でstr型(文字列型)変数mystringを宣言し、
"This is my string!"
で"This is my string!"(を指す値)で初期化しています。
この変数をそのままprintのs出力で出力しています。
このようにstr型の変数の使い方の基本は
・str型変数に文字列を割り当てる。
・割り当てた文字列を使いたいところに変数名を記述する。
だけです。意外とシンプルですね。
ただしこのままではstr型変数の存在意義がよくわかりませんね。
実際には配列とループ文を組み合わせてよく使うのですが、このことはループ文の章で紹介します。(その9)
(ここから先は余談です。)
str型変数に文字列を代入するという覚え方でもACSの作成に支障はありませんが、厳密に言うと違います。
実はstr型はもともと整数を扱う型なのです。(bool型も実はそうです。)
次の例を見てみましょう。
間違った例
#include "zcommon.acs"
Script 1 ENTER
{
print(s:"You got" + "the shotgun!");
}
このACSを実行すると、"You got the shotgun!"という文字列が画面に表示されそうですが、
実際は"the shotgun"と表示されます。
これは、ZDOOMの文字列の管理方法に理由があります。
さきほどの例のACSをコンパイルするとき、文字列は次の様に管理されます。
文字列 | 番号 |
"You got" | 0 |
"the shotgun!" | 1 |
... | 2へと続く... |
(ただし、同じ文字列にコンパイラが出会っても新しい番号は割り振りません。)
(#include "zcommon.acs"は特殊な指令なので除外されます。)
このように、文字列には上から順番に0から番号が与えられていき、文字列の引き渡しはこの番号によって管理します。
つまり、str型は何を取り扱っているかと言うと、
変数に代入された文字列に割り振られた番号
という整数値を取り扱っています。
また、printのs指定も
渡された整数値に対応する文字列を出力する。
という意味を持っています。
つまり、先ほどの例の
print(s:"You got" + "the shotgun!");
は、表より、"You got"に0、"the shotgun!"に1を代入して
print(s:0 + 1);
となり、
print(s:1);
となります。ここで、番号1が割り当てられた文字列は"the shotgun!"なので、結果的に"the
shotgun!"が画面に表示されます。
ちなみに、次の様な等号関係は成り立ちます。
例
#include "zcommon.acs"
Script 1 ENTER
{
str MyStrings =
"equal";
print(d:MyStrings == "equal");
}
この場合、MyStringsと後に出てくる"equal"は同じ番号が割り当てられているので等号関係が成り立ち、画面に1と表示されます。
先ほど述べたように、str型は(bool型も)整数値を扱うので、かけ算や割り算も行うことができます。
しかし、もともとの使い方ではないのでそのような使い方は避けましょう。誤解を招きかねません。
(余談終了)
演算子
演算子とは前にも紹介した「+,-,*,/」といったものです。
ZDOOMは多くの種類の演算子をサポートしています。
まず、演算子の紹介の前に、演算子の性質の説明をします。
「+,-,*,/」と言った演算の種類を示すものをオペレータ(演算子)
2 * 3 の2,3のように、演算子によって演算されるものをオペランド
と言います。
ここで、
- 1
2 * 2
のように、オペレータによって必要となるオペランドの数が変わってきます。
このような演算方法の違いを見てみましょう。
(軽く読み流して下さい。)
二項演算
二つのオペランドを使用して計算をします。
ZDOOMで、二項演算の形は
(オペランド) (オペレータ) (オペランド)
//2 * 3など
の形となります。
また、ZDOOMで使用する二項演算子は、複数つなげて多項演算を行うこともできます。
//2 * 3 * 5など
特に、二項演算の時に、オペレータの右のオペランドを「右オペランド」、左のオペランドを「左オペランド」と呼んだりします。
単項演算
一つのオペランドを使用して計算をします。
ZDOOMで、単項演算の形は
(オペレータ) (オペランド)
//- 1など
の形となります。
代入演算
文字通り変数に整数を代入する演算です。
=
が代入演算子(オペレータ)です。
左辺が変数で、右辺が整数(や変数がある式)であるようにしましょう。
算術演算
通常の+,-などのオペレータを使った演算方法です。
四則演算や剰余演算などに使用されます。
関係演算
2つの整数の大小関係や等号関係を演算する方法です。
>や<などが関係演算子です。
例えば
a <
3
などが関係演算です。
(条件文の章で紹介しましたね。)
ビット演算
2つの整数(または変数)をビット演算する場合、
各整数をビットで表して、
2つの整数のそれぞれ同じ場所のビットについて順番にすべてのビットを計算をします。
int型の整数の演算に向いています。
AND演算を例にとってビット演算を見てみましょう。
AND演算とは、電気回路などでも出てくるように
ある二つの一桁の2進数に対して、&演算子を用いて
式 | 結果 |
1 & 1 | 1 |
1 & 0 | 0 |
0 & 1 | 0 |
0 & 0 | 0 |
というような結果を返す演算子です。
これを10進数にも応用します。
5 & 7
という式を考えてみると
それぞれ2進数(ビット)に表現し直して
101 & 111
という形にします。
それぞれの桁について、先ほどのAND演算を行うと、
101
という結果が出ると思います。
これを10進数に直して
5
という値が帰ってきます。
つまり、
5 & 7
を計算すると
5
という結果が出ます。
また、AND式と同じように、OR式が存在し、|演算子を用いて、
式 | 結果 |
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
と表すことができます。
この&演算子、|演算子などを応用すると、ビットの並びとしてのint型と組み合わせることができます。
例えば、
一つのint型(xとします。)に32個のスイッチがあって、右から3つめの設定をON(=1)にしたいときには
x = x | 4
//2進数に直すと
//x | 100
//この結果の値をxに代入する。
とすると、ONにできます。
逆に、右から3つめの設定がON(=1)かどうかを調べたいときには
x & 4
とすると結果の値が0でなければ設定がONになっているということがわかります。
このように、ビット演算は、ACSを作っていく上で重要な存在となっています。
論理演算
bool型の変数の演算方法です。
2つの整数(または変数)を論理演算する場合、
それぞれの整数の事柄が真(TRUE)か偽(FALSE)かで演算をします。
このとき、整数の値が0の場合は、偽(FALSE)と認識され、
0以外の場合は負の数の場合でも真(TRUE)と認識されます。
また、事柄には演算子を使った式を持ってくることができます。
例えば
bool
a = 1,b =
0;
とし、論理演算をするときは、
aは真(TRUE)の事柄でbは偽(FALSE)の事柄です。
また、
int monsters
= ThingCount(0,999);//ここではtidが999のモンスターの数を数える役割の関数
とし、論理演算をするときは、
monstersに代入された値が0のとき(tidが999のモンスターが全滅したとき)、
変数monstersは偽(FALSE)の事柄となり、
monstersに代入された値が1以上のとき(tidが999のモンスターが生存しているとき)、
変数monstersは真(TRUE)の事柄となります。
演算子表を添付しますので、必要であれば見て下さい。しかし必要になったときに見るだけで十分だと思います。
演算子表へ
定数
定数は一見変数と似ていますが、変数とは全く別の概念です。
しかし、覚えていると非常に便利なので紹介します。
複数の場所に(特定の意味を持った)同じ数字を配置する場合、
その値を定数として扱うと楽になることがあります。
次の例を見てみましょう。
#include "zcommon.acs"
#define Pi 3.1415
Script 1 ENTER
{
print(s:"Pi is ",f:Pi);
}
と表示されます。
最初の赤い字の部分を見てみましょう。
例のように、定数の宣言方法は
#define 定数名 値
と宣言します。(この文をdefine文と呼ぶことにします。)
そして定数を呼び出すときは、定数名を書くだけでその値を呼び出すことができます。
実際にはコンパイラがACSをコンパイルするときに、
この定数の宣言文よりも後の部分で同じ定数名が存在した場合、その名前と実際の値を入れ替えてコンパイルします。
つまり、定数はPiはコンパイル時には3.1415という値に変わります。
このように定数という概念を使うと(例えば円周率をPiと定義しておけば)円周率を毎回書かなくてもただPiと書くだけで円周率を呼び出すことができ、
非常に便利です。
また、円周率の精度を上げたいときに(円周率の値を変更したいときに)
define文の部分のみを変えればすべての部分のこの結果が反映され、すべての箇所の修正も必要なく、非常に便利です。
また、ACSでは、はじめからサポートされている定数が存在しますが、それは使っているうちに覚えると思うので紹介は省略します。
例を挙げるなら、YESが1、NOが0などです。
include文
さて、さきほどの定数宣言で
#define
というキーワードが出てきましたね。
このように、#(シャープ)が文頭に来るような場合、そのフレーズを前処理指令と言って、
コンパイラがACSをコンパイルするときに参照される内容です。
#(シャープ)がつくキーワードは
#define
以外にもありましたね。
#include
です。(今後このキーワードをinclude文と呼ぶことにします。)
今回は先ほどの例の青い字の部分を見ていきましょう。
#include "zcommon.acs"
include文の意味は大体「次に記すファイル名をコンパイル時に参照せよ」と言う意味です。
つまり、
"zcommon.acs"
にはACSのコンパイルに必要な内容が入っています。
一体どのような内容があるのでしょうか。
実際にこのファイルを見てみましょう。
コンパイラが存在するディレクトリにzcommon.acsがあるので、テキストエディタを使って開きます。
すると、コメント文と一緒に次の様な文があります。
#include "zspecial.acs"
#include "zdefs.acs"
#include "zwvars.acs"
またまたinclude文が出てきました。そしてコンパイラが存在するディレクトリに上記の3つのファイルが存在するので、それぞれ見ていくと、
次の様な役割があることがわかります。
zspecial.acs | Action specialsが定義されています。(特殊な定義のされ方です。) |
zdefs.acs | ACSがあらかじめ定義している定数(YESやT_IMPなど)です。ここに新しく定数を宣言することもできます。 |
zwvars.acs | スコープ(後に解説)がworld型のオリジナルの変数をここで宣言します。※未確認 |
これらの3つのACSファイルはあまり意識的に使うことはありません。
知っていなくてもACSを作るときに
#include "zcommon.acs"
と記述すれば自動的に参照されます。
覚えなくてもACSを作る支障とはなりませんが、こんな物もあるんだという程度で覚えていて下さい。
とりあえず、このフレーズを必ず記述しなければACSがコンパイルできないということを覚えておいて下さい。
まとめ
・int型には整数、ビットの並び、文字、固定小数点数としてのはたらきがある。
・int型には表現できる整数値の限度がある。
・ビットの並びとしてのint型は「スイッチ」の役割を持っている。
・固定小数点数の表記の仕方に注意(3.0など)
・bool型は「0でない」か「0である」かが重要。
・str型変数には、文字列に割り当てられた番号を代入されている。
・演算子はその都度覚える(^_^;)
・定数の宣言方法は
#define 定数名 値
・定数を宣言した行より後の行で定数が使用できる。
・include文という存在があり、#inludeや#defineを前処理指令という。