ZDOOMHEXEN.PRJの解説4
DOOM2.PRJではThingの種類は、モンスター、アイテム、置物、テレポート先等限られていましたが、ZDOOMHEXEN.PRJでは様々な役割を持ったThingを取り扱うことができます。このページではその一部を紹介します。

ACSを同時に利用していきます。


このページで取り扱う内容

Camera   静止、回転、移動するCameraの作り方をそれぞれ紹介します。
SecurityCamera(静止) 静止しているCameraを作ります。
SecurityCamera(回転)   その場で回転するCameraを作ります。
AimingCamera その場で特定の物体を見つめるCameraを作ります。
MovingCamera 動くCameraでカメラワークを作ります。
 
PatrolPoint   モンスターが特定の道筋を移動するようにします。
   
PolyObjects   壁などをXY平面方向に移動させます。
回転する壁   PolyObjectsを使って回転する壁の仕掛けを作ります。
引き戸 横にスライドするドアを作ります。
開き戸 回転するドアを作ります。
 
Upper/LowerStackLookOnly 視覚的な立体交差を作ります。
 
Skybox   カメラでリアルタイムに撮っている画像を空に貼り付けます。
   
HateTarget   モンスターの攻撃対象を設定します。
   
Spark   (音付き)火花を散らします。
   
SectorActions   SectorActionsの種類・意味を紹介します。

Camera

Cameraを使うことで、プレイヤーの視点以外から物を見ることを可能にします。Cameraを実際に設置してみましょう。

SecurityCamera(静止)

その場を動かないまたはその場を回転するCameraを作ります。
題名にも書いてあるとおり、SecurityCamera +Pitch-Angle-SpeedというThingを使用します。(Typeは9025番)

まず、その場を動かないCameraを設置してみましょう。

図の様にSecurityCameraを南向きに設置したとします。
(CameraのThingの向きはそのままカメラの視線の方向と同じになります。つまり、Cameraが南を向きます。)
引数Pitch,Angle,Speedはすべて0にして下さい。

これでSecurityCameraの設置は完了です。次に起動しましょう。
ACSからCameraを起動するのが便利だと思うので、ACSからCameraを起動します。
このMAP用のACSを作成して、次の文章を書き加えて下さい。


//Cameraを起動するACS1
#include "zcommon.acs"

Script 1 ENTER
{
ChangeCamera (2, 0, 0);//TidはSecurityCameraと同様の物にして下さい。
}

上記のACSを見てわかるとおり、関数ChangeCameraは、Playerの視点を別の視点に変える関数です。(LineTypeは237番)
関数ChangeCameraのそれぞれの引数の意味は↓

引数 内容 入力する値の形式 補足
tid 映像を画面に映したいCameraのTid。 integer -
who この関数の起動主のみにこの結果(映像)を反映するかすべてのPlayerに反映するか。 integer 0→起動主 / 1→全員
revert 起動主が動いたら視点をもとに戻します。 integer 0→戻さない / 1→戻す

このACSをMAPに挿入してテストプレイをしてみましょう。

視点がカメラに変更されています。成功です。

しかし、このままだと、永遠にPlayerの視点に戻れないので、Playerの視点に戻れるようACSを修正しましょう。


//Cameraを起動するACS2
#include "zcommon.acs"

Script 1 ENTER
{
ChangeCamera (2, 0, 0);//TidはSecurityCameraと同様の物にして下さい。

delay(35*5);//5秒間待機

ChangeCamera (0, 0, 0);//Tid 0 → Playerの視点
}

5秒後に視点をPlayerに戻します。このように、ChangeCamera (0, 0, 0)と入力することで、視点をもとに戻すことができます。
実際に確かめてみて下さい。


SecurityCamera(回転)

次に、その場で回転するCameraを設定してみましょう。SecurityCameraの引数を設定することでCameraが回転します。
先ほどのSecurityCameraの編集画面を開いて、引数を変更しましょう。
それぞれの引数の意味は↓

引数 内容 入力する値の形式 補足
Pitch 仰角。 integer (下向き) 89〜1 (真ん中 = 0) 255〜166 (上向き)
Angle Cameraの向きから左右に回転する角度。 integer(度数法) -
Time 一端から反対側へ回転するために必要な時間。 integer(Octic) 8 octics = 1 秒

つまり、SecutiryCameraを
仰角0°で、3秒で180°回転させたいときは、
Pitch→0
Angle→90
Time→24
にしましょう。

カメラの起動方法は先ほどと同じです。

ちなみに、回転しないCameraでも、引数Pitch単体を使うことができます。


AimingCamera

AimingCamera +Pitch-maxAngle-maxPitch-oTidというThingを使ってCameraを起動すると、その場で指定したThingを見つめ続けます。Thingが動いたら、Cameraも回転します。(Typeは9073番)

AimingCameraを使って、Playerを見つめ続けるCameraを設定してみましょう。
先ほどのMAPのSecurityCameraをAimingCameraに変更して下さい。
AimingCameraの引数を設定することで、Cameraの詳細設定を行います。↓

引数 内容 入力する値の形式 補足
Pitch AimingCameraの最初の仰角。 integer (下向き) 89〜1 (真ん中 = 0) 255〜166 (上向き)
maxAngle AimingCameraが1秒間に左右方向に回転できる最大の角度。 integer(度数法) -
maxPitch AimingCameraが1秒間に上下方向に回転できる最大の角度。 integer(度数法) -
oTid 見つめる対象のTid。 integer -

今回は
Pitch→0
maxAngle→90
maxPitch→24
oTid→255(Player)
にします。
しかし、PlayerはTidが与えられていないので、ACSでPlayerのTidを設定する必要があります。
ACSを次の様に書きましょう。


//Cameraを起動するACS3
#include "zcommon.acs"

Script 1 ENTER
{
ChangeCamera (2, 0, 0);//TidはAimingCameraと同様の物にして下さい。
}

Script 999 ENTER
Thing_ChangeTid(0,255);

//PlayerのTidの変更
//Enter型のScriptなので、この関数の起動主はPlayerです。

/*
関数Thing_ChangeTID (oldtid, newtid)(LintType176番)
引数oldtid→Tidを変えるThingのもとのTid
引数newtid→変更後のTid
注意:oldtidに0が入力されると、関数Thing_ChangeTIDが適応される対象はこの関数の起動主になります。
*/

Script999でPlayerのTidを255番(oTidと同じ値)にしました。

実際にテストプレイをしてみましょう。


移動

成功です。


MovingCamera

動けるCamera(カメラワーク)を作ってみましょう。有名なPWADファイルには高い割合でMovingCameraが使われていますよね。
今回は動けるCameraとして、Moving Camera +lNode-hNode-bits-tracerというThingを使用します。(Typeは9072番)
また、MovingCameraの道筋を示す必要が出てきます。Interpolation Point +Pitch-travTim-holdTim-lTid-hTidというThingでMovingCameraの道筋を指定します。(Typeは9070番)

完成予定図

Interpolation Pointを配置する前に、Moving Cameraをどこでも良いので、配置して下さい。


ここで、MovingCameraの引数を設定します。↓

引数 内容 入力する値の形式
lNode MovingCameraの最初のスタート地点のInterpolation PointのTid1(※1)。 integer
hNode MovingCameraの最初のスタート地点のInterpolation PointのTid2(※1)。 integer
bits MovingCameraの諸設定。(※2) integer
tracer AimingCameraのようにTidを指定したThingを見つめます。(0なら何も見つめません。)(※3) integer

※1
lNodeとhNodeを使ってInterpolation PointのTidを指定します。
(目的のTid) = lNode + hNode * 256
となるようにlNode,hNodeの値を指定しましょう。
ちなみに、何故このような事を行うかというと、Thingの引数全般の定義域は0〜255(整数)なので、それよりも大きい値のTidを指定するときのためです。

※2
bitsには下の表の設定したい項目の値の和(ビット論理和)を入力します。
例えば、値2と値4の項目を設定したいときはbitsには6を代入します。(2+4)

効果
1 MovingCameraがカクカク動きます。(設定しないならスムーズに動きます。)
2 MovingCameraがInterpolation PointとInterpolation Pointの間を移動しているときに、
次のInterpolation Pointの向きに合わせるように左右方向にゆっくり回転しながら移動します。
4 MovingCameraがInterpolation PointとInterpolation Pointの間を移動しているときに、
次のInterpolation Pointの仰角に合わせるように上下方向にゆっくり回転しながら移動します。
8 値2または値4の項目が設定されているとき、MovingCameraは移動中に移動している方向を向くようになります。
128 すべてのplayerにこのMovingCameraの映像を適応します。

ちなみに、値2と値4の設定がされないとき、MovingCameraは最初の向きを保ったまま移動します。

※3
引数tracerを利用する場合、引数bitsの値2と値4の項目は無視されます。
AimingCameraの様に、回転する速さの上限を指定することはできません。


今回は、
lNode→100
hNode→0
bits→6 (= 2 + 4)
tracer→0
にします。


次に、Interpolation Pointを動かしたい道順になるように配置します。

今回は図の様に3個配置しました。基本的に何個置いても正しい道順を指定すれば正しくMovingCameraは動作します。

図の様に、Interpolation PointのTidをそれぞれ100,101,102にしました。
ここで、MovingCameraのlNodeに100を代入したので、MovingCameraの移動開始地点はTidが100のInterpolation Point(図の上部)になります。

Tidが100のInterpolation Pointの引数を設定してみましょう。


Interpolation Pointの引数は↓

引数 内容 入力する値の形式 補足
Pitch Interpolation Pointの仰角。 integer (下向き) 89〜1 (真ん中 = 0) 255〜166 (上向き)
travTim MovingCameraが次のInterpolation Pointまで
移動するのに必要な時間。
integer(Octic) 8 octics = 1 秒
holdTim MovingCameraがこのInterpolation Pointに到着したとき、
MovingCameraが停止している時間。
integer(Octic) 8 octics = 1 秒
lTid 次のInterpolation PointのTid1(※) integer -
hTid 次のInterpolation PointのTid2(※) integer -


MovingCameraのlNodeとhNodeのときと同様に、
(目的のTid) = lTid + hTid * 256
となるようにlTid,hTidの値を指定しましょう。


今回、Tidが100のInterpolation Pointの引数を
Pitch→0
travTim→24
holdTim→0
lTid→101
hTid→0
にしました。


図に書いてあるとおりに、同様の動作をTidが101,102のInterpolation Pointにも行って下さい。

Tidが102のInterpolation PointのlTidを100としたので、MovingCameraは三角形を描きながら永遠に回り続けます。
ここで、MovingCameraがループするようにInterpolation Pointを配置しないとMovingCameraが動こうとしないことに注意して下さい。

これで設定は完了です。ACSで起動させましょう。


//Cameraを起動するACS4
#include "zcommon.acs"

Script 1 ENTER//マルチプレイヤー時はOPENのほうが良いかもしれません。
{
ChangeCamera (2, 0, 0);//TidはMovingCameraと同様の物にして下さい。

delay(1);//これがないと何故か動きません。

Thing_Activate(2);//これでMovingCameraを動かします。
}

ACSを見てわかるように、Thing_Activate関数で、止まっているMovingCameraを移動するようにします。
途中でMovingCameraを止めたくなったら、Thing_Deactivate関数を使って止めます。

ACSに書いてあるとおり、delay関数を記述しないと上手く動きませんでした。
(OPEN型やvoid型のScriptにしたらdelay関数なしでも正常に作動しました・・・不思議ですね(^_^;))

これですべての設定が完了しました。テストプレイをしてみましょう。


数秒後

無事カメラワークができました。(ちなみに、引数tracerが0なので、このCameraはplayerを見つめているわけではありません。)


PatrolPoint

PatrolPointを使用すると、モンスターに特定の場所へ移動するよう指示することができます。

PatrolPointを使ってモンスターをパトロールさせてみましょう。

Patrol Point (Path Node) +TidNext-DelayというThingを使って、モンスターの移動地点を指定します。(Typeは9024番)

完成予定図

図の様に、モンスターが正方形を描くように移動させます。

まず、モンスターを配置します。モンスターのTidを今回は3にします。
モンスターが移動するのをPlayerが安全に観察できるように、モンスターのFriendlyの項目にチェックを入れて下さい。
これでモンスターはPlayerを襲いません。

モンスターを移動させたい地点にPatrol Pointを配置しましょう。
今回は4個配置しました。また、図の様に、Tidをそれぞれ200〜203にしました。

Patrol Pointの引数を設定しましょう。Tidが200のPatrolPointを選択して、編集画面を開きます。


Patrol Pointの引数は↓

引数 内容 入力する値の形式 補足
TidNext 次のPatrol PointのTid。(※) integer -
Delay モンスターがこの一つ前のPatrolPointに来たときに、待機する時間。 integer(秒) -


私のPCの場合、Patrol Pointの編集画面を開いたときに、第一引数であるTidNextの名前が表示されませんでした。
名前が表示されなくても第一引数の役割は変わりません。
MovingCameraの時のように、lNodeとhNodeは存在しません。Patrol PointのTidが0〜255の間に収まるようにMAPを作って下さい。


今回、Tidが200のPatrolPointの引数を
TidNext→201
Delay→0
にしました。


残りのPatrolPointも図を参考にして同様に引数の値を指定します。

ちなみに、MovingCameraの時のように、進路がループするようにPatrol Pointを設置する必要はありません。
その場合、モンスターが最後のPatrol Pointに到着したら、そこで移動を終了します。

これで設置は完了です。次にACSでモンスターを誘導させましょう。


//Patrol Point
#include "zcommon.acs"

Script 1 ENTER//マルチプレイヤー時はOPENのほうが良いかもしれません。
{
Thing_SetGoal (3, 200, 0, 1);
}

ACSに書かれてあるとおり、関数Thing_SetGoalを使ってモンスターに移動する指示を与えます。


関数Thing_SetGoalの一般的な形は↓

Thing_SetGoal (tid, goal, delay, dontchasetarget) (LineType229番)

引数は↓

引数 内容 入力する値の形式 補足
tid 移動させるモンスターのTid。 integer -
goal 最初に移動させるPatrol PointのTid。 integer -
delay モンスターが最初に動き始めるまでにかかる時間。 integer(秒) -
dontchasetarget Patrol Pointを移動中に敵と遭遇したら進路を離脱してモンスターを襲うかどうか。 integer 0→離脱する
それ以外→進路を維持

ちなみに、dontchasetargetでモンスターの進路を維持させるようにしても、進路を維持しながら攻撃してきます。
また、dontchasetargetで離脱するように指定しても、敵を倒したらモンスターは元の進路に戻ります。


モンスターをまずTidが200のPatrol Pointに移動させたいので、引数goalの値は200にしてあります。

これですべての設定が完了しました。
テストプレイをしてみましょう。


数秒後

少しオートマップが見えにくいですが、モンスターが正しく移動しています。成功です。


PolyObjects

壁を構成するLineはxy方向(東西南北方向)には移動できない仕組みにWADファイルはなっていますが、PolyObjectsを使用すると、そんな制限を打ち破ることができます。(それでも制限はありますが・・・)今回は、回転する壁と引き戸と開き戸を作ってみましょう。

回転する壁

PWADで遊んでいると、壁がぐるぐる回っている仕掛けをたまに見かけますよね。今回はそれを作ってみましょう。

PolyObjectsを作るために、PolyObj Anchor +PolyidとPolyObj Start Spot+PolyidというThingを使います。(Typeはそれぞれ9300,9301)
PolyObj AnchorからPolyObj Start Spotの方向へ、回転させたい壁を平行移動させる役割を持っています。

とりあえず作ってみましょう。

図の様に、図の右部分の中央の4本のLineを回転させます。
この4本のLineにはSide2を持たせないで下さい。また、両方の部屋の床、天井それぞれの高さを同じにして下さい。

まず、回転させたいLineのTextureを好きな物に変更します。

次に、図の様にPolyObj AnchorとPolyObj Start Spotを配置します。
ここで、どちらか一方を選択して、編集画面を開きます。PolyID/Angleの項目に1を入力して下さい。(PolyIDはTidみたいな物です。)
両方ともPolyID/Angle(以後Polyid)に1を入力して下さい。

次に、どのLineがPolyObjectsか指定する必要があります。回転させたいLineの4本のうちどれか1本(図では緑色のLine)を選択し、LineTypeを
B Poly Objects→1 Poly StartLine +Poly-Mirror-Sound [1]
にします。(これが記念すべきLineType1番なのですね(^_^;))
関数Polyobj_StartLineの詳細はこちらのLineType1番。

先ほど選択したLineの引数を変更します
Poly→1
Mirror→0
Sound→0(音無し)
にしてください。

これでとりあえずテストプレイをしてみましょう。

Playerがいる部屋に無事動かしたい壁が平行移動されました。しかし、回転していません。
ACSを使って回転させましょう。


#include "zcommon.acs"

Script 1 OPEN
{
Polyobj_RotateLeft (1, 32, 255);
}

関数Polyobj_RotateLeftの詳細はこちらのLineType2番

Polyobj_RotateLeft (1, 32, 255)としましたから、PolyIDが1番のPoly Objectが左回りに永遠に回ります。

では、テストプレイをしてみましょう。


回転

左の画像の描写が若干おかしいですが、無視して下さい(^_^;)

とりあえず成功です。

ちなみに、回転させたい壁はSide2を持ってはいけないと記述しましたが、Side2を持たせる場合は、回転させたいLineすべてを選択して、LineTypeを
B Poly Objects→5 Poly Explicit Line +Poly-Order-Mirror-Sound [5]
にする必要があります。
関数Polyobj_ExplicitLineの詳細はこちらのLineType5番
関数Polyobj_ExplicitLineの説明にも書いてあるとおり、Orderは1から順番に値を代入して下さい。


引き戸

PolyObjectsを使って引き戸を作ってみましょう。

図の様に、東に開くドアが引き戸の東側に、西に開くドアが引き戸の西側に来て、Playerはドアを起動するとドアは左右にスライドする仕掛けです。
今回も、ドアとなるLineにはSide2を持たせません。
また、PolyObj Start Spotは一つのSectorに対して1個しか存在できません。図の様にPolyObj Start Spotを別々のSectorに存在するようにして下さい。

ドアとなるLineにそれらしいTextureを貼り付けます。

次に、図の様にPolyObj AnchorとPolyObj Start Spotを配置します。PolyIDもそろえてください。

まずは、ドアを目的地まで平行移動させましょう。@とAのLineの編集画面を開きます。LineTypeを
B Poly Objects→1 Poly StartLine +Poly-Mirror-Sound [1]
にします。

とりあえず引数PolyにそれぞれのPolyIDを入力して下さい。(後でまた編集します。)

これで両方のドアの目的地への平行移動が完了しました。次に、ドアを開閉させるようにしましょう。
鉛直方向に開閉するドアと同じように、ドアとなるLineに「ドアを開ける」LineTypeを貼り付けます。
すなわちBとCのLineが「ドアを開ける」LineTypeを貼り付ける対象となります。

BのLineとCのLineを選択し、変編集画面を開き、LineTypeを
B Poly Objects→8 Poly Door Slide +Poly-speed-Angle-Distance-Delay [8]
にします。

関数Polyobj_DoorSlideの詳細はこちらのLineType8番

BのLineは東に開くドア用のスイッチなので、
Poly→2
speed→64
Angle→0(東)
Distance→64
Delay→35
にしましょう。

CのLineは西に開くドア用のスイッチなので、
Poly→3
speed→64
Angle→128(西)
Distance→64
Delay→35
にしましょう。

LineTypeの起動条件は「スイッチ式・使用できる回数無限」にしてください。

これでドアが動くようになりました。実際にテストプレイをしてみましょう。


東のドアを起動

図の様に、片方のドアを起動すると片方のドアしかスライドしません。同時に両方スライドさせるようにしましょう。
ここで登場するのが、関数Polyobj_StartLineの引数mirrorです。(@のLineとAのLineのLineType)
引数mirrorの意味は、「反対の動作を行うPolyObjectsのPolyID」でした。
つまり、東側のドア(PolyID2)が開くときに東側のドアのPolyobj_StartLineの引数mirrorの値を3(西側のドアのPolyID)にしておくと、
西側のドアは東側のドアが開くと同時に西(東の反対側)にスライドします。

つまり、@のLineの引数mirrorを3に、AのLineの引数mirrorを2にします。
これでテストプレイをしてみましょう。


ドアを起動

無事両方のドアが同時に開きました。

しかし、全く音が鳴りません。少し不自然ですよね。次はドアの開閉音を出せるようにしましょう。

ドアのような連続した音(例えば、リフトの場合、リフトが下がっている音とリフトが止まる音が連続しているなど)を定義するためには、
SpecialLumpファイルの一種であるSNDSEQを使用する必要があります。

テキストファイルを新規作成して、次の内容を書き加えて下さい。(タイトル名は何でもかまいません。)


//SNDSEQ

//ドアが開く音
:SlidingDoorOpen
door 2
playuntildone doors/dr2_open
stopsound plats/pt1_stop
end

//ドアが閉まる音
:SlidingDoorClose
door 3
playuntildone doors/dr2_clos
stopsound plats/pt1_stop
end


//ドアの連続する音の定義
[MyDoor1
door 1 //このdoor 1 の値を使用します。
0 SlidingDoorOpen
1 SlidingDoorClose
]

↑ちなみに、今回はZDOOMにはじめから搭載されている音を使いましたが、
オリジナルの音を使用したいときは、SNDINFO(SpecialLumpファイル)も使用する必要が出てきます。

簡単に説明すると、
SlidingDoorOpenで
ドアが開く→ドアが開ききる
という音の一連を定義しています。
SlidingDoorCloseも同様にドアが閉まるときの定義をしています。
MyDoor1でSlidingDoorOpenとSlidingDoorCloseを組み合わせて、ドアの開閉という一連の流れの音を定義しています。
ここで、door 1 としているので、SNDSEQで定義した音の一連の流れは、音番号1になりました。

このテキストファイルを保存し、こちらのSpecialLumpファイルの挿入方法に従って、このテキストファイルを挿入します。
(挿入時の名前は"SNDSEQ")これで、ドアの開閉時の音の新しい組み合わせがWADファイルに挿入されました。

ここで、@とAのLineTypeであるPolyobj_StartLineを思い出して下さい。
引数にsoundがありましたよね。ここに入力する値は、先ほどの音番号です。
今回は音番号1で定義したので、両方のLineの引数soundに1を入力します。

実際にテストプレイをしてみましょう。音が鳴れば成功です。


開き戸

次に、開き戸を作ってみましょう。作り方は基本的に引き戸の時と変わりません。

図の様に、片開きの開き戸を作ります。
今回も、ドアとなるLineにはSide2を持たせません。
また、観音開きの開き戸を作る場合、PolyObj Start Spotは一つのSectorに対して1個しか存在できないことにも注意して下さい。

まず、ドアとなるLineにそれらしいTextureを貼り付けます。

次に、図の様にPolyObj AnchorとPolyObj Start Spotを配置します。
PolyID(今回は4)もそろえてください。
※注意:設置したPolyObj AnchorとPolyObj Start Spotを軸にしてドアが回転するので、ドアの回転軸となる地点にPolyObj AnchorとPolyObj Start Spotを配置して下さい。

次は@のLineを選択し、編集画面を開いてLineTypeを
B Poly Objects→1 Poly StartLine +Poly-Mirror-Sound [1]
にします。
引数Polyに4(PolyObj AnchorとPolyObj Start Spotと同じPolyID)を入力します。
観音開きの開き戸を作る場合は、引き戸の時と同じように引数mirrorで反対側のPolyIDと対応づけて下さい。
今回は引数soundに0を入力します(効果音を使いません)。ドアの開閉音を入れたいときは、引き戸の時のような作業を行って下さい。

これでドアが目的地まで平行移動されました。次はドアを開けるLineTypeをAのLineに貼り付けましょう。

AのLineを選択し、編集画面を開いて、LineTypeを
B Poly Objects→7 Poly Door Swing +Poly-Speed-Angle-Delay [7]
にします。

関数Polyobj_DoorSwing の詳細はこちらのLineType7番

今回は、反時計回りに90°回転させたいので、それぞれの引数について、
Poly→4
Speed→8
Angle→64 (度数法で90°)
Delay→70
とします。

LineTypeの起動条件は「スイッチ式・使用できる回数無限」にしてください。

これでテストプレイをしてみましょう。

無事開き戸が完成しました。
ちなみに、左上の画像の様に、ドアが両端の壁にぴっちり挟まっていても、引っかかることなく開閉します。

注意:PolyObjectsを使うとよく画像描写がおかしくなります。対処方法はまだよくわかりません・・・(^_^;)


Upper/LowerStackLookOnly

解説3で立体交差があるMAPを作りましたが、少し設定がややこしかったですよね。
しかし、Upper/LowerStackLookOnlyを使うことによって、(視覚的のみ)より簡単に立体交差を表現することができます。
視覚的なので、立体交差が行われている上下のSectorを上下に行き来することは基本的にできません。
(上下間で行き来できるようにするためには、少し工夫が必要になります。)


↑こんな感じに視覚的に立体交差を作ります。上下のSector間を繋げている隙間に飛び降りても上下間で移動はできません。

今回は、Upper Sector +Flat TransparencyとLower Sector +Flat TransparencyというThingを使用します。(Typeはそれぞれ9077番,9078番)
この2つのThingを使うことによって、簡単に視覚的な立体交差を作ることができます。では実際に作ってみましょう。

図の様に、西のSectorを上の階に、東のSectorを下の階にし、それぞれの天井、床の高さを図の様にします。
上の階、下の階のそれぞれの真ん中のSectorは吹き抜けとなり、両方の階を見ることができます。
(実際にテストプレイをしてみると、これらのSectorは重なっているように見えます。)
吹き抜けとなる両方のSectorは合同な関係にあるようにしてください。
また、両方のSectorのつなぎ目となる天井または床の高さは同じ値になるようにしてください。
(つまり、西の真ん中のSectorの床の高さと東の真ん中のSectorの天井の高さが同じでなければなりません。)
ここで、下の階のSectorで、真ん中のSectorと外側のSectorの天井の高さの差(16units)は、上の階の床の厚みになります。

それぞれのLineにそれらしいTextureを指定した後に、上の階の真ん中のSectorにはUpper Sectorを、下の階の真ん中のSectorにはLower Sectorを配置しますが、それぞれ存在するSectorに対して、Upper SectorとLower Sectorが全く同じに位置になるように配置して下さい。
(ずれて配置すると、上下間の描写がずれて行われてしまいます。)
それぞれのTidを共通の番号にして下さい。(今回は4)

これでテストプレイをしてみましょう。

←上の階から
(この穴に飛び込んでも下には落ちません。)
←下の階から
(こちらも同様です。)

視覚的な立体交差が上手く行われています。成功です。
しかし、鉛直方向にこれらのSectorは重なっていないのにそう見えてしまうのは不思議ですよね。

立体交差作成に成功しましたが、このままでは穴に飛び込んだらそのまま落ちてしまいそうな気がします。
半透明な板を間に挟んであげましょう。

西の真ん中のSectorの床の画像と、東の真ん中のSectorの天井の画像を間に挟みたい板の画像にします。
Upper Sectorを選択し、編集画面を開き、引数Flatに透明度(0透明〜255不透明)を入力します。今回は128を入力しましょう。
こうすることで、上の階から下の階を見たときに、間に半透明な板が挟まっているように見えます。
同様の動作をLower Sectorに対しても行います。

テストプレイをしてみましょう。

←上の階から
←下の階から

間にガラスのような物が挟まりました。成功です。

簡単にリアルな立体交差を作ることができますが、遠く離れて吹き抜けを見ると描写がおかしくなったりすることがよくあります。注意して下さい。

補足:図の様に、吹き抜けとなるSector内に新たにSectorを作る場合は、念のため、図の様に、Upper SectorとLower Sectorを付け足して下さい。


Skybox

空の絵を変えてみたいと思ったことはありませんか?Skyboxを使うことでCameraから撮影したリアルタイムの映像が空に描写されます。使い方はとてもシンプルです。

今回は空に巨大なImpが出現する空の映像を作ってみましょう。

今回使用するThingはSkybox View point (camera)とSkybox Picker +SkyTidというThingです。(Typeはそれぞれ9080番、9081番)

図の様に、東側のSectorのSkybox View pointで撮影した映像を西側の空のTextureに貼り付けます。
Skybox View pointがCameraの役割で、Skybox Pickerがその映像を貼り付けるSectorを指定する役割を持っています。

図の様にSkybox View pointとSkybox Pickerを東向きに配置します。(ZDaemonで動かす場合、東向きでないと不具合が発生します。)
映像を貼り付けたいSectorが複数ある場合は、その数だけそれぞれのSectorにSkybox Pickerを設置します。
Skybox View pointのTidを5にしたとします。Skybox Pickerの引数SkyTidを5にしましょう。
このように、Skybox View pointが複数存在しても、特定の映像を空に貼り付けることができます。

ではテストプレイをしてみましょう。

無事にリアルタイムにSkybox View pointからの映像が空に貼り付けられています。成功です。
ちなみに、空の画像として扱われるので、どの位置から空を見ても向いている方角が同じであれば全く同じ映像を見ることになります。


Hate Target

関数Thing_Hateと組み合わせることによって敵を特定の地点に攻撃させることができます。
(実は関数Thing_Hateは私がZDOOMHEXEN.PRJを使い始めたときに最初にしたかったことです(^_^;))

まず、関数Thing_Hateについて紹介します。
関数Thing_Hateは、特定のモンスターの攻撃対象をPlayerから他の物体に変更する関数です。(変更対象が味方の場合でも攻撃します。)
関数Thing_Hateの一般的な形はThing_Hate (hater, hatee, hate type)で、引数の詳細は↓


引数 内容 入力する値の形式
hater 攻撃対象を変更するモンスターのTid。 integer
hatee 攻撃対象のThingのTid。 integer
hate type 攻撃の種類。(※) integer

※引数hate typeに入る値と意味

意味
0 攻撃対象のThingが視界に入らなくても活動を開始し、攻撃対象を攻撃します。
他のThingからの攻撃があった場合は、攻撃対象がそちらに変わります。
攻撃対象が消滅した場合、攻撃対象はPlayerに戻ります。
1 攻撃対象のThingが視界に入るまで活動を停止し、攻撃対象が視界に入れば活動を始め、攻撃対象を攻撃します。
他のThingからの攻撃があった場合は、攻撃対象がそちらに変わります。
攻撃対象が消滅しても、Playerが攻撃しない限り、Playerを襲いません。
2 攻撃対象のThingが視界に入らなくても活動を開始し、攻撃対象を攻撃します。
他のThingからの攻撃があった場合は、攻撃対象がそちらに変わります。
攻撃対象が消滅しても、Playerが攻撃しない限り、Playerを襲いません。
3 攻撃対象のThingが視界に入るまで活動を停止し、攻撃対象が視界に入れば活動を始め、攻撃対象を攻撃します。
他のThingからの攻撃があった場合は、攻撃対象がそちらに変わります。
攻撃対象が消滅した場合、攻撃対象はPlayerに戻ります。
4 0の時と同じです。
5 攻撃対象のThingが視界に入るまで活動を停止し、攻撃対象が視界に入れば活動を始め、攻撃対象を攻撃します。
攻撃対象が消滅しても消滅しなくても何があってもPlayerを襲わなくなります。
6 攻撃対象のThingが視界に入らなくても活動を開始し、攻撃対象を攻撃します。
攻撃対象が消滅しても消滅しなくても何があってもPlayerを襲わなくなります。

次に、Thing Hate + Angle=hate#というThingについて紹介します。(Typeは9076番で、今後HateTargetと呼びます。)

このThingは透明で、このThingをダミーの攻撃対象にすることで、モンスターはあたかもHate Targetが置いてある地点を攻撃しているかのように見えます。

HateTargetは引数を持っているように見えますが、引数を使用しません。
HateTargetは、PolyID/Angleに入力した10倍の値の体力を持ちます。(0の時は不死身です。)
モンスターがその体力以上を攻撃したとき、Hate Thingは消滅します。

では、実際にこの仕掛けを作ってみましょう。
まず、攻撃させたいモンスターを配置し、Tidを(仮に)7とします。
攻撃させたい地点にHateTargetを設置し、PolyID/Angleの値を変更して自由に体力を変更して下さい。(今回は0にします。)
HateThingのTidを(仮に)6とします。

ACSで関数Thing_Hateを使用します。


//Thing Hate
#include "zcommon.acs"

Script 1 OPEN
{
Thing_Hate(7,6,6);//Tid7のモンスターがTid6のThingに攻撃。
}

テストプレイをしてみましょう。

HateTargetを置いた地点が集中砲火を浴びています。成功です。


Spark

時々火花が散っているような演出があるPWADを見かけますよね。今回はその仕掛けを作ってみましょう。

火花を散らすThingとして、Sparks +countというThingを使用します。(Typeは9026番)

このThingから火花が飛び散ります。(音付きです。)

Sparksは引数countを持っています。引数countに一回で飛び出る火花の数を入力して下さい。(今回は32。もし0を入力すると自動的に32になります。)
Sparksの向きは、火花の飛び散る方向を表します。飛び散らせたい方向を指定しましょう。
SparksのTidを(仮に)8とします。

ACSを使って火花を飛び散らせます。


//Sparks
#include "zcommon.acs"

Script 1 OPEN
{
Thing_Activate(8);//火花を飛び散らせます。
delay(Random(7,35));//0.2秒〜1秒の間隔で火花が出ます。
restart;//このScriptを繰り返します。
}

ACSに書かれてあるとおり、関数Thing_ActivateでSparksを起動したときに火花が飛び散ります。
上のACSの様に、乱数を使ってランダムな間隔で火花を出すとよりいっそうそれらしい雰囲気が出ると思います。

テストプレイをしてみましょう。

無事火花が出ました。成功です。


Sector Actions

Sectorにドアの開閉などのLineTypeの役割を持たせることはできませんでした。しかし、SectorActionsを使うことで、それを可能にします。

LineTypeを持たせたいSectorの中にSectorActionsのThingを入れて、そのThingのSpecialで希望のLineTypeを指定すればSectorからドアを開閉させるなどの指示を与えることができます。

SectorActionsの種類は↓

Class名(正式名称) DeePseaでの名前 LineTypeが起動される条件 Type 起動主の切り替え
SecActEnter Actor enters sector 物体がSectorの中に入ったとき。 9998 あり(※1)
SecActExit Actor leaves sector 物体がSectorから外へ移動したとき。 9997 あり(※1)
SecActEyesAboveC Eyes go above fake ceiling Playerの目が偽物の天井(※2)を下から上へ移動したとき。 9982 なし
SecActEyesBelowC Eyes go below fake ceiling Playerの目が偽物の天井(※2)を上から下へ移動したとき。 9983 なし
SecActEyesDive Eyes go below fake floor Playerの目が偽物の床(※2)を上から下へ移動したとき。 9993 なし
SecActEyesSurface Eyes go above fake floor Playerの目が偽物の床(※2)を下から上へ移動したとき。 9992 なし
SecActHitCeil Actor hits ceiling 物体が天井に接したとき。 9996 あり(※1)
SecActHitFakeFloor Actor hits fake floor 物体が偽物の床(※2)とぶつかったとき。 9989 あり(※1)
SecActHitFloor Actor hits floor 物体が床に着地したとき。 9999 あり(※1)
SecActUse Player uses sector Sector内でPlayerがUseキーを押したとき。(※3) 9995 なし
SecActUseWall Player uses wall Sector内でPlayerがLineに向かってUseキーを押したとき。(※3) 9994 なし

※1
SectorActionsの設定を変えることで、起動主になりうる物体の種類を拡張することができます。
SectorActionsのDormantにチェックを入れると、ミサイル系の攻撃弾も起動主となります。
SectorActionsのDeafにチェックを入れると、モンスターも起動主となります。

※2
解説3の水たまりのような床や天井のことです。

※3
PlayerがUseキーを押したとき、目の前のLineにLineTypeが設定されいていればそちらの起動が優先されます。


ZDOOMHEXEN.PRJの解説はここで終了です。ZDOOMHEXEN.PRJの解説に目を通していただき、ありがとうございました。


必要に応じて内容を追加、変更します。


Home Back