メイン | May 2005 »

April 29, 2005

鏡もち

鏡もちヘルスパック


クリックで拡大

随分と昔に作ったやつで、しかも季節もかなり違いますが一応・・・ということで。ヘルスパックが鏡もちになります。それだけです。導入はpk3をetmainかお好きなMODフォルダに入れてくれればおしまいです。

ファイル

ゴールデンウィークということで小旅行へいってきますv(^-^

投稿者 ikanatto : 11:10 AM | コメント (0)

April 28, 2005

ETPRO 3.1.12 & 3.1.13 Features & Fixes 和訳

ETPROが新しくなって何が変わったのか、についてを和訳してみました。特に真新しい部分もなく面白みもないのですが、興味などございましたらごらんください。
翻訳元及びetproダウンロード

ちなみにETPROのバージョン表示についてですが、一番最初の数字がメジャーバージョン、次が「パブリック向け」のマイナーバージョン、最後が開発チーム内やベータなどでのマイナーバージョン(つまり正式ではない)、場合によってはこの後bugfix x.xなど内部バージョンが含まれています。

ETPRO Bug Fixes & Features


3.1.12
バグ修正・チート対策

cg_autoactionがスクリーンショットの際に正常に働かなくなっていた問題を修正。
opentimerinputはマウスフォーカスをしなくなりました。
敵の負傷した(腹筋中の)プレイヤーが蘇生の障害となっていた点を修正。
レフリーによる降参が誤って投票になってしまう点を修正。
cg_teamchatsonlyが全体チャットをブロックしなかった点を修正。
shuffleteamsxp_norestartが投票メニューに表示されていなかった点を修正。
ファイアチームのアイコンが若干ずれて表示されていた問題を修正。
cl_languageが変な翻訳文を表示していた点を修正。
inviteallが既にファイアチームに入っているプレイヤーにも影響していた点を修正。
etproのアンチラグ機能が向上。
全ての武器の発射率とオーバーヒートはFPSに依存しなくなります。
b_fallingbugfix 1はフェイクブラシを無視しなくなりました。
ファイアチームのアイコンはb_fireteamAlphaに従って設定されます。
oasisのスクリプトが修正されました。
チャット中での地名は色が変わらなかった点を修正しました。
負傷したCVOPSが相手の服を奪えた点を修正。
血痕がクラッシュおよびハングを引き起こしていた問題を修正。
プレイヤーがリンボーになった状態でも死体が射線を遮っていた点を修正。
cg_teamchatsonlyがファイアチームチャットをフィルターしていた問題を修正。

Known bugs:
None

3.1.13
バグ修正・チート対策

サーバーがクラッシュする問題を修正。
clientkickが異なるクライアントを蹴りだしていた問題を修正。
誤ってレフリーがXP SHUFFLE NO RESTARTをコールしてしまう問題を修正。
ポーズをかけたときのバグを修正。
ダイナマイト設置・解除、オブジェクト強奪・奪還のログとアナウンスを修正。
b_wolfrofはRtCWのmp40と同じ発射率になりました。

新機能

ET 2.60に対応(ぶっちゃけ2.60にのみ対応)。

Macに対応。

アンチリコイルとセンタービュースクリプトも感知可能。

ヒットサウンドは武器によって鳴る鳴らないが決まります。

/guidsはスクリプト解析のためにヘッダーとフッターを出力します。
freecamSetPosは次の二種類の構文による使用が可能です。
freecamSetPos x y z
freecamSetPos x y z pitch yaw roll

b_noactivateleanによってactivateとstrafeの同時押しでleanになることを抑制できます。
b_drawspectatorteamflagsによってスペクテイター中HUDにチームの旗を表示できます。
b_flushitems for ground-oriented items (medpacks, ammopacks)
b_simpleitems by fretn
surrender(降参)がコールできます。(vote_allow_surrenderによって許可不許可)
コンソールからプライベートメッセージが送れるようになりました。
shuffleteamsxp_norestartが追加され、マップをリスタートすることなくシャッフルが可能です。
fireteam inviteallはファイアチームに入っていない全ての人を招待します。
fireteam inviteは離れた名前にも対応します。
手榴弾を持った状態でaltweaponするとvsay_team FireInTheHoleと同じ動作をします。

組み込み式スパウンタイマー
/timerSetで設定します。
/openTimerInputによってインターフェイスを開きます。
/resetTimerでリセットします。

New headbox code
新しいヘッドヒットボックスコード
b_realhead 1によって新しいコードを適用できます(+256,+512,+1024,+2048を追加することでデバッグモードになります)。
注意1:伏せた状態でのボディのボックスはヒットではなく衝突判定のために使われているので誤って表示されるかもしれません。
注意2:デバッグモードに入ったら、g_smoothclients 0も使ってください。でないとデバッグボックスの表示が変になるかもしれません。

Location names
位置名表示機能を全面的にサポートします。クライアントは_loc_override.datを読み込もうとし、これを_loc.datの上書きデータとします。どちらのファイルも見つからない場合、自動的に位置周辺の物を名称として生成します。

標準6マップについてはデフォルトで位置名ファイルを含めています。

位置表示に関するコマンド
loc_add , loc_del (operates on closest), loc_rename (closest), loc_save [full], loc_load, loc_show (closest).

Cvars:
b_emptyscript
最後のプレイヤーがサーバーから抜けたときに指定したスクリプトが実行されます。

b_defaultbantime x -
明確な時間設定を必要としないban時間を秒で設定します。(デフォルト300秒)

b_wolfrof x -
SMGの時間当たり発射率を変更します。0はET標準の発射率(デフォルト)、1でRTCWの(mp40)発射率になります。

b_shrug x -
/shrugを有効無効にするかです。0で無効(デフォルト)、1で有効になります。

b_distancefalloff x -
距離によるダメージ減少の有効無効。0はなし、1は有効(デフォルト)。

b_locationMode x -
位置表示に関する設定です。0で無効、1で位置の名前を表示、2は座標を表示、4はPVSのチェックなし、8は位置名が見つからなかった時に座標を返すことを抑制します。

b_debuglocations x (cheat) -
位置表示に関するデバッグ表示の設定です。1は位置マーカーに泡スプライトを表示します。(緑=最短、赤=それ以外)。2は1が泡ではなく光になります。4はマーカの上に名前を表示します。8は他のプレイヤーによる位置変更を受け入れます。16は他のプレイヤーへ位置情報を送ります。

投稿者 ikanatto : 04:46 PM | コメント (13)

April 27, 2005

Urban Terror for Enemy Territory FAQ

ET:UrT FAQ

Urban Terror for Enemy Territoryに関するFAQを和訳してみました。待ち遠しくて仕方がない私とあなた、しばらくは彼等の頑張りを見守りつつETを遊びましょう。そもそも彼等は趣味でやっているだけなので強要できないのですが、それでもできるだけ早く作って欲しいと願うのは人のサガでしょうか。

Urban Terror for Enemy Territory FAQ

略記:UrT(Urban Terror) ET(Enemy Territory) SD(Splash Damage) Q3(Quake 3 Arena) CoD(Call of Duty)

URBAN TERROR FOR ENEMY TERRITORY FAQ(update 04-08-04)

ETって何ですか。

ウルフェンシュタインシリーズの最新作品です。AlliesとAxisに別れ、つながりのあるマップ群を戦いぬくものです。

UrTはスタンドアロンですか。

id softwareと協議した結果スタンドアロンにはなりません。ETとUrTを別々に導入してください。

頒布ファイルサイズはどれくらいになりそうですか。

約260MBになりそうです。

Macintoshにも対応しますか。

今のところ対象外です。しかしコーダーがSDと話し合っていますので、結果が出次第追ってWebで発表します。

ET:UTにはマップがいくつ含まれますか。

12個を予定しています。この中にはAbbey2、Casa、Druglord3、Prague、Turnpikeが含まれます。

32人以上のサーバーを立てるのはいかがでしょうか。

プログラム面で問題はありませんが、マップが16人を上限にデザインされているので16〜20を限度にした方がよいでしょう。

ストレイフジャンプのように、Q3と物理演算は同じですか。

はい。

他のエンジンにUrTを移植予定はありませんか。

ありません。

Q3でET版のUrTをプレイできますか。

できません。

ETエンジンは適切なスケルトンアニメーションがないのでは。

新たなプレイヤーモデルを作る必要があります。武器もマップもコンバートしなければなりません。コードは似ているのですが、それでも2ヶ月以上かかる見通しです。

TeamKillはETのように扱われるのでしょうか。

3回TKするとサーバーから蹴りだされます。

全ての作業が終わるまでに6ヶ月程かかるのではと見込んでおりますが。

今のところ少なくとも3ヶ月以上かかるものとみています。

Q3ではなくETに移行するメリットとは。

最初にして最大の点が、ETがフリーウェアということです。

Q3よりも優れたエンジンが登場した時UrTはどうなるのでしょうか。

ゲームシステムが大きく変わることはないでしょう。ただ、マップ、アニメーション、ヒット判定が大幅に改良されるなどは期待されます。

ET:UTはパンクバスターのサポートを得ますか。

はい。

ET:UTではゲームモードの追加及び削除はありますか。

現在話し合っていますが、追加される見込みはなさそうです。

ETのようなクラスの概念はありますか。

その予定はありません。もともとUrTは選択する武器と装備で性格づけが可能ですので、改めて追加する必要もないのです。

ETのソースコードがGPLライセンスになった場合、UrTのコードも公開されますか。

いいえ。

CoDにUrTの移植してくれませんか。

ETに移植するきっかけは、id softwareとSDと仲がよかったこと、そして無料であることです(CoDはInfinity Wardと連絡をとっていないため面倒)。

UrTをETへ移植することはSIDにどのような影響をもたらしますか。

MOD発表当初よりid softwareとSD(アマチュアのころ)と連絡を取り続けています。UrTをETに移植することでこれを多くのゲームコミュニティに紹介することが可能です。すでにいくつかの将来的なタイトルに向けたアイデアがありますが、これを実現する機会が得られていない状況です。UrTの移植により、アイデアを実現させるための接触が得られると考えています。

投稿者 ikanatto : 06:37 PM | コメント (16)

ETpro crashes ET servers and administrators

 先日リリースされたETPRO、ベータ版という開発者の意図に呼応したかのように各所でクラッシュを起こしている。
 具体的に何が問題になっているかは定かではないが、今まで正常に稼動していたサーバーが特にコンフィグを調整することもなく動かしてのクラッシュということでETPRO自体が原因だろうか。

 ただし、クライアントサイドとWindows系ではこの問題は生じていないようである。クライアントサイドではプレイヤーをキックする機能にバグがあるようだが、それ以外はおおむね問題なく機能している。ただし、機能がバランスを保持しているかは別問題であり、ROFなどゲーム性を変化させるフィーチャーに関しては(?)である。RtCWにはRtCWの、ETにはETの味があるのではなかろうか。
 [N]さんの暇つぶしサーバも例外ではないようで、既にET2.56へバージョンを差し戻している。前バージョンが非常に安定であっただけに今回の騒動は非常に残念である。
 各地のフォーラムを見て回った限り、クラッシュを起こしている多くがLinux系Dedicated。ログを確認すると、大抵の場合インターミッションを終えてキャンペーンないしマップを読み込みなおす時にクラッシュが生じているようである。またマップ終了時に出力されるリザルトも若干表示がおかしいことも報告されている。開発者側の考えではパンクバスターとの相性不良によって生じているらしい。

投稿者 ikanatto : 05:45 PM | コメント (13)

April 26, 2005

ETpro available for ver 2.60

ET Pro 3.1.12

ETPROの2.60対応版がベータながら出たようです。

ETPro Forums News

バージョンはET Pro 3.1.12 となっています。

大きな問題はないようですが、サーバーに記録されるログが正常に出力されない模様です。

Readme Overview

バグフィックスなどは特筆すべき部分は少ないでしょうか。腹筋中のプレイヤーが蘇生やダイナマイト解除の障害となる部分が修正されたことはチェックするべきかもしれません。

2.60完全対応ということでMacに対応しています。当然といえば当然です。

注目すべきは、リコイルキャンセルとセンタービューのスクリプトもETproによって排除可能になったというところでしょうか。個人的にはこれにはあまり賛同できませんが、必要とする人もいるようなのでようやく、といったところです。ただETProが何をセンシングしてスクリプト判定を下しているのかだいたい予想はつくので、この裏をかいたスクリプトがいずれ登場するやもしれません。

頭のヒットボックスに関するコードがまた変わったそうです。どうなったのでしょうね?

マップの位置と名称を別ファイルで定義することで完全対応させられるようです。面倒ですが価値はあるのかもしれません。

あとは新規に追加された機能関係のCvarがいくつか新たに登録されています。発射率をRtCWに変更するものもあるようです。あとサーバーに誰もいなくなったときにスクリプトを走らせるCvarも追加されています。なかなか心憎い機能です。

以下は上記フォーラムからの転載です。

Version 3.1.12
cg_autoaction was broken for screenshots
opentimerinput doesnt get mouse focus
enemy wounded could block team wounded for revive
ref surrender menu callvoted by mistake
cg_teamchatsonly didnt ignore public chats
shuffleteamsxp_norestart was missing from vote menu
fireteam objective icon was misaligned slightly
cl_language could cause garbage translations
inviteall tried to invite players already on a fireteam
etpro unlagged accuracy increased
all weapon rof / overheat are now completely fps independent
b_fallingbugfix 1 no longer ignores fake brushes
fireteam objective icon now obeys b_fireteamAlpha
oasis script fixed (remove end of round delay after destroying both guns)
location names didn't reset color in chats
wounded covops could steal uniforms if close enough
bloodtrail crash/hang fixed
corpses could be "shot" after players tapped out
cg_teamchatsonly incorrectly filtered fireteam chats

New features:

ET 2.60 support (in fact, 2.60 only)

Mac OSX support

Anti-recoil and centerview scripts defeated

hitsounds are now generated for hitscan weapons only

/guids now outputs a header/footer for easier script parsing

freecamSetPos now supports two syntaxes:
freecamSetPos x y z
freecamSetPos x y z pitch yaw roll

b_noactivatelean (default 0, disabled) to enable/disable strafe+activate=lean
show team flag on hud while spectator following, b_drawspectatorteamflags to enable/disable
b_flushitems for ground-oriented items (medpacks, ammopacks)
b_simpleitems by fretn
callvote surrender (vote_allow_surrender enables/disables)
console can now send pm's
shuffleteamsxp_norestart to shuffle teams without restarting the map
'fireteam inviteall' to invite all players on your team who arent already on a fireteam
fireteam invite can now match on partial names
altweapon while holding grenade will vsay_team FireInTheHole

builtin spawntimer:
/timerSet to set it (eg from a script)
/openTimerInput for a ui popup
/resetTimer to reset the timer

New headbox code
new headbox code, enabled through b_realhead 1 (+256,+512,+1024,+2048 enables various hitbox debugging modes)
note: debugging body box for prone may "appear" wrong because its the box used for player collisions, not shots.
note2: if you use one of the debugging modes, use g_smoothclients 0 as well, or the debugging boxes will "appear" wrong.

Location names
Added full location name support - client will attempt to load from _loc_override.dat (for user's own location names) followed by _loc.dat (for server-supplied location names in e.g. a pk3), and failing both, client will build locations from target_location entities in the map.

Included rough draft versions of locations for all 6 stock maps.

Quick summary of location code:

Commands:
loc_add , loc_del (operates on closest), loc_rename (closest), loc_save [full], loc_load, loc_show (closest).

Cvars:
b_emptyscript
Name of a script to execute when the last player leaves the server

b_defaultbantime x -
Time in seconds for bans without explicitly defined ban time (default 300)

b_wolfrof x -
0 = ET Standard SMG Rate of fire (default
1 = RTCW Standard SMG Rate of fire

b_shrug x -
0 = Disable /shrug (default)
1 = Enable /shrug

b_distancefalloff x -
0 = Disable distance damage falloff for bullet weapons
1 = Enable distance damage falloff for bullet weapons (default)

b_locationMode x -
0 = Disabled
1 = Location names (Default)
2 = Location coordinates
4 = Don't check PVS when finding nearest location name
8 = Don't fall back on coordinates when no location found (return "unknown")

b_debuglocations x (cheat) -
1 - Draw bubble sprites at location markers (green = nearest, red = others)
2 - Draw dlights at location markers (red = nearest, green = others)
4 - Float location names at location markers (currently no scaling)
8 - Accept location updates from other players
16 - Send location updates to other players

8 + 16 simply bounce the command to other players on a loc_add/del/rename, so that multiple people can work on marking up a level with location names.

投稿者 ikanatto : 12:17 AM | コメント (21)

April 24, 2005

Conception of mds/mdm/mdx importer

MDS/MDM/MDX importer for 3ds max

This plan is still conceptional. I have done a little about this, scratching some ideas and learning c++ from c :p Arrrgg, Object orientation??? what the hell is this...

I cant even figure out how long this work takes till an available version comes. God may only know. Or even he doesn't.

What this plugin does

The final target of this plugin concept is free and easy input/output between 3ds max and ET(RtCW). There are some formats for ET and they have different import/export gateways. Yes, this is REALLY BOTHERING. One more thing I have to say, there is no tools to export/import mdm or mdx. Whoever should be blamed, this environment is sucked for all modders as wll as me.

Eh, simple reasons why I try this. Let you guess a sec.

Model Formats in ET

MD3 is a versatile format to the quale3-engine-based games and used for various things. In ET, MD3 is common object models for map objects, weapons(partly there are some MDCs), accesseries and so on. MD3 can include animations.

MDM is a mesh data however MDM itself can not be used as model data. Combination of MDM and MDX makes a model. MDM has only vertex weights and texture coordinates and tags. MDX has bones and thier animations. Normally most humans have same bones( i think you have two arms and legs, and a head, dont you? ) so ET handles one mdx to all models and saves memory.MDM and MDX are used for player models.

MDS is used in RtCW. In fact, MDS = MDM + MDX however tags are somewhat different. Still they are almost same. The problem of MDS is to consume quite memory. Each MDS has animation data, which is ordinally enormus. So loading many MDSes leadd to high memory consumption.

投稿者 ikanatto : 12:07 AM | コメント (132)

April 22, 2005

W:ETソースコード朗読会 補足2

Reading the Source Code of W:ET Appendix #2

The world of an 'Entity'

みなさんこんにちは。挨拶しても恐らく誰もここを見ていないのが寂しいところです。しかしめげずに今回もソースコードを読んでいきます。

この回で取り上げるのは実際の処理部分ではなく、entityという構造体です。Quake3のコード提供を受けて開発されたゲームのほぼ全てはこの構造体も継承しています。それだけ重要な部分ということですから、ソースコードを読む立場の我々もこれを知っておく必要があるでしょう。

An Entity

entityという英単語はなかなか目にしません。辞書で意味を調べると、「(実在する)物;(他から独立した)存在(物)」と、実にあいまいな記述にとどまっています。字面をそのまま解釈すれば、要するに何か「もの」を指す便利な言葉で、thingに近いのではないでしょうか。

例えばあなたが手榴弾を投げますと、gentity_tという型を持ったg_entities配列の中の使われていない一つが選択され、手榴弾の情報が書き込まれます。書き込まれる内容は、移動速度、向き、投げた人、ヘルス、弾性、大きさ、見た目、炸裂した時の音、ダメージ量、炸裂範囲、爆発するまでの時間、爆発時間に達した時に呼び出される関数などなど、この手榴弾に関するあらゆる情報が格納されます。

武器に限らず、ゲーム中に登場するほぼ全てのものがg_entitiesによって管理制御されています。プレイヤーもその一つです(厳密にはクライアントはentityに加えてplayerstate_tなどを含むgclient_tによって制御されます)。

結局のところ、entityとはつまり何でもありで、とにかくゲーム中登場する全てのものを一元管理するアイデアの下に作られた構造体であるものでしょう。そこはカーマックさんに尋ねてみないと真実はわかりません。しかし、拡張性の高いシステムであることには間違いないので、さすがはカーマックさんといったところです。スーパープログラマーとはかくありきですね。

A Structure 'entity_t'

概念的なことばかり紹介してもイメージが掴みにくいかもしれませんので、実際どのように定義されているのかを見てゆきましょう。gentity_t構造体の定義はg_local.hにあります。213行以降です。g_entitiesの宣言はg_main.cの最初あたりにあります。まずはg_main.cからみましょう。

gentity_t		g_entities[MAX_GENTITIES];

冒頭近傍でこのように宣言されているかと思います。ちなみにMAX_GENTITIESは別のところで定義されております。

#define	GENTITYNUM_BITS		10
#define	MAX_GENTITIES		(1<<GENTITYNUM_BITS)

なぜ定数定義にビット演算を用いているのか甚だ疑問ではございますが、結局MAX_GENTITIESは2の10乗、つまり1024です。つまり1024個のentityを同時に扱うことが出来るわけです。

ではgentity_tの定義をみてみましょう。

struct gentity_s {
	entityState_t	s;				// communicated by server to clients
	entityShared_t	r;				// shared by both the server system and game
	// DO NOT MODIFY ANYTHING ABOVE THIS, THE SERVER
	// EXPECTS THE FIELDS IN THAT ORDER!
	//================================
	struct gclient_s	*client;			// NULL if not a client
	qboolean	inuse;
	
	(省略)

	// bleh - ugly
	int		backupWeaponTime;
	int		mg42weapHeat;
	vec3_t	oldOrigin;
	qboolean runthisframe;
	g_constructible_stats_t	constructibleStats;
};

かなり大きく複雑な構造体になっております。もしC++で組まれていたらもう少しスマートなクラスとして定義できたかもしれませんね。

まずは最初の部分をみてみましょう。

	entityState_t	s;				// communicated by server to clients
	entityShared_t	r;				// shared by both the server system and game

注釈を見るとわかるとおり、サーバー通信機能とゲーム処理部分によってsとrが区別されています。しかしながらどちらもentityの基本的な情報を含んでいることには変わりません。具体的には位置、速度、向き、種類、大きさ、イベントなどなど。サーバーとクライアントでやり取りされている部分でもあります。

	struct gclient_s	*client;			// NULL if not a client
	qboolean	inuse;
	vec3_t		instantVelocity;	// ydnar: per entity instantaneous velocity, set per frame
	char		*classname;			// set in QuakeEd
	int			spawnflags;			// set in QuakeEd

大抵のものには注釈がついており、ほとんどの場合その通りの使われ方をしています。clientはentityがクライアントかどうかについてのもので、もしそうならクライアント情報を含むアドレス、そうでないなら0となります。inuseはこのentityが使用中かどうかのフラグです。instantVelociityは注釈どおりフレームにおける瞬間速度です。classnameとspawnflagはマップオブジェクトに対して定義され、マップエディタ中で設定した値がここに入ります。

これ以外もentityの性質、状態、動きに関するデータが並んでいます。この中で注目すべきはイベント発生に関する部分です。

	int		nextthink;
	void		(*free)(gentity_t *self);
	void		(*think)(gentity_t *self);
	void		(*reached)(gentity_t *self);	// movers call this when hitting endpoint
	void		(*blocked)(gentity_t *self, gentity_t *other);
	void		(*touch)(gentity_t *self, gentity_t *other, trace_t *trace);
	void		(*use)(gentity_t *self, gentity_t *other, gentity_t *activator);
	void		(*pain)(gentity_t *self, gentity_t *attacker, int damage, vec3_t point);
	void		(*die)(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod);

nextthinkは設定されたイベントが発生するまでの時間で、ミリ秒が単位です。以下に並ぶのが各イベントが発生した場合に呼び出される関数のアドレスです。freeはこのentityが開放される、つまりinuseになる時にコールされます。thinkは設定した時刻が経過した場合に呼び出されます。手榴弾が5秒後に爆発するのはこのためです。その他各イベントにも対応した処理が自動的に行われます。なかなか興味深い部分です。

具体的な例として、g_weapon.cの2237行に空爆缶に関するコードがありますので見てみましょう。

void weapon_checkAirStrikeThink1( gentity_t *ent ) {
	if( !weapon_checkAirStrike( ent ) ) {
		ent->think = G_ExplodeMissile;
		ent->nextthink = level.time + 1000;
		return;
	}

	ent->think = weapon_callAirStrike;
	ent->nextthink = level.time + 1500;
}

関数の引数としてentというentityが渡されます。これは空爆缶を指しているentityです。if文は空爆を行うことが出来る条件のもとに空爆缶が投げられたかを見ています。そうでないならこの空爆缶は空爆をせずにG_ExplodeMissileという単に爆発するだけの処理をlevel.time + 1000ミリ秒後、つまり一秒後に行います。条件が満たされた場合、空爆缶は空爆処理を行うweapon_callAirStrikeを1.5秒後に呼び出します。

Entity is Everything

これまでみたように、entityとはつまりなんでもありということが理解してもらえたかと思います。またデータでなくイベント自動処理に関する項目も含まれており、いかにゲーム処理アルゴリズムの大きなところを担っているかもお分かりなったのではないでしょうか。

今回紹介した部分以外にもコードに登場する構造体はたくさんあります。クライアント、アイテム、武器、マップ、コンフィグなどなど色々なものに構造体が定義されています。新しくMODを作る場合にはこれらの理解が欠かせませんので、そのおつもりのある場合は是非一見なさることをお勧めします。

投稿者 ikanatto : 06:11 PM | コメント (110)

April 21, 2005

Urban Terror Equipments

Equipment selection

Urban Terror(以下UrT)では、プレイヤーがどのような武装をするかは範囲内で任意に決めることが出来ます。まとめると以下の表のように選択できます。

メイン(サイド含む)1つ、準メイン一つ、ハンドガン1つ、装備品一つ

メイン1つ、ハンドガン1つ、装備品2つ

サイド一つ、ハンドガン1つ、装備品2つ

ハンドガン1つ、装備品3つ

具体的なパターンとして、M4 Assault Rifle+MP5K+Beretta+HE Grenadeなんかをチョイスすることでひたすら撃ちまくるスタイルをとることが出来ます。またHK69+DE+HE Grenade+追加弾薬で走る爆弾魔になることもできます。

私が好きな組み合わせは、M4+DE+Flash Grenade+防弾チョッキと防弾ヘルメットで閃光突撃型、FFAなんかだとSR-8+UMP45+DE+暗視スコープでオールラウンド型です。このあたりはその人の個性が良く現れます。また武器にこだわりのある場合もあるので、なかなか見ているだけでも楽しいものです。

Urban Terror Fire-arms

そんな銃撃戦を彩るUrTで登場する火器を紹介します。紹介するとはいったものの、私は実在の火器類にはまったく詳しくないのでゲームに登場する武器としての説明になります。実際の性能などについては検索することで容易に知ることができるかと思います。

ZM300

もともとUrTには有名なM4 Assault Rifleがあったのですが、そちらがモデルチェンジしてこちらになりました。性能はM4とまったく同じです。非常にバランスの取れた銃で、突撃などの近距離、遭遇戦といった中距離から遠距離まで全てに対応できます。困ったらこれを選べばまぁ間違いはないでしょう。

装填弾数30・威力○・連射○・集弾○

アタッチメント:サイレンサー、レーザーサイト

分類:メインウェポン

AK103

私の中では、テロリスト定番の銃という印象があります。これも非常にバランスのよい銃ですが、若干M4よりも威力が高いかもしれません。気のせいかもしれません。サイレンサーはつきません。

装填弾数30・威力○+・連射○・集弾○

アタッチメント:レーザーサイト

分類:メインウェポン

G36

これはかなり面白い武器です。何が面白いかというと、狙撃銃としてマシンガンとしても使える点です。そのため遠距離から近距離まで、全てのレンジがこの武器の得意領域になります。若干威力が低い気もしますが、それでもやはり面白い武器です。

装填弾数30・威力○-・連射○・集弾○

アタッチメント:サイレンサー

倍率:2倍まで

分類:メインウェポン

PSG-1

UrTにも近代を舞台にしたガンシューティングお決まりの狙撃銃があります。そのうちの一つがこのPSG-1です。SR-8と比較した時に最も異なるのが、狙撃銃ながらもセミオートマチックなのでセカンドショットを素早く行える点があります。その為初弾で倒せなくとも、次の攻撃により倒すことが可能になります。

装填弾数5・威力◎・連射△・集弾―

アタッチメント:サイレンサー

倍率:2倍・4倍・6倍

分類:メインウェポン

SR-8

狙撃銃の代表格です。絶大な威力を誇るため、防弾チョッキを着た相手でもほぼ瞬殺することが可能です。手足に当たった場合でも体力を8割以上奪えます。欠点は次の弾を撃つまでに時間がかかること、近接には対応できないことです。しかしそれを補って余りある威力はやはり魅力的です。

装填弾数5・威力◎◎・連射×・集弾―

アタッチメント:なし

倍率:2倍・4倍・6倍

分類:メインウェポン

HK69

特徴的な火器です。弾を飛ばす点では他の火器と変わりませんが、飛んでいく弾が実は爆弾なのです。遠くに飛ばせる手榴弾と考えるとわかりいいでしょうか。俗に言うグレネードランチャーです。こちらの武器はかなり威力が調整された歴史があり、それだけ使い勝手のいいものです。飛ばす距離も段階的に調整できます。これをマスターしてあなたもジェド・豪士!?

装填弾数4・威力◎◎◎・連射×・集弾―

アタッチメント:なし

分類:メインウェポン

Negev

一言でいえばマシンガンです。それも並ではなく、凄い連射できるマシンガンです。150発分の弾丸が装填されているので、思いの丈連射できます。その代わりに集弾性が犠牲になっていますので、使い方にちょっと工夫がいるでしょう。

装填弾数150・威力○・連射◎・集弾△

アタッチメント:レーザーサイト

分類:メインウェポン

MP5K

コンパクトなマシンガンです。そのため威力がかなり見劣りしますが、連射性能に関しては他のマシンガンと変わりません。ヘッドショットを積極的に狙いたいところです。

装填弾数30・威力△・連射○・集弾○

アタッチメント:サイレンサー・レーザーサイト

分類:サイドウェポン

UMP45

サイドウェポンのマシンガンながら威力は高めです。その代わりに連射性と弾数が少し少なくなっていますので、MP5Kとどちらを選ぶかは個人的な趣味の違いになると思います。

装填弾数25・威力○・連射△・集弾○

アタッチメント:サイレンサー・レーザーサイト

分類:サイドウェポン

SPAS12

ショットガンです。ただし扱いがサイドウェポンの関係上、全弾命中して相手を一撃で倒せる程度の威力になっています。しかしこのショットガンの驚くべき特徴として、非常に連射性が高い点が挙げられます。このため間合いの近い状態なら非常に有利に戦うことが出来るでしょう。欠点を挙げるとするならヘッドショット判定がないことです。

装填弾数8・威力○・連射△・集弾×

アタッチメント:なし

分類:サイドウェポン

Beretta

ごく普通のハンドガンです。その為威力は控えめですが、15発装填されていますので意外に戦えます。

装填弾数30・威力△・連射△・集弾◎

アタッチメント:サイレンサー・レーザーサイト

分類:ハンドガン

Desert Eagle

でっかいハンドガンです。でかいので威力がすごいことになっており、M4よりも強力な一撃を見舞うことが出来ます。場合によってはこの武器で大活躍してしまうかもしれません。

装填弾数7・威力◎・連射△・集弾○

アタッチメント:レーザーサイト

分類:ハンドガン

Urban Terror Gear

UrTはその他プレイヤーを特徴付けるものとして、装備品を選択することが出来ます。選択できる装備品は最大で3個で、選択した武器の個数によって増減します。

Kevlar Vest

いわゆる防弾チョッキです。胴体へのダメージを軽減し、流血を防ぎます。ただしSR-8のような強烈な弾丸は貫通してしまいます。

Kevlar Helmet

防弾ヘルメットです。チョッキと同じく頭部へのダメージを軽減し、即死を回避することができますが、強力な武器に対しては意味がありません。

HE Grenade

手榴弾です。炸裂した周囲のプレイヤーにダメージを与えます。近ければ即死です。

Flash Grenade

閃光榴弾です。炸裂した瞬間に周囲のプレイヤーの視力を奪います。視力が回復するまでの時間は、閃光弾との距離と向きによって変わります。

Smoke Grenade

煙幕弾です。これが炸裂した周囲に濃い煙を発生させてプレイヤーの視野を狭くします。

Laser Sight

レーザーを照射することによって狙いを定めるものです。火器の集弾性が向上します。

Silencer

火器の射撃音を軽減します。若干集弾性も向上します。レーザーサイトと同時に使うこともできます。

Night Vision Goggles

暗視スコープです。これを使うことで暗闇に潜むプレイヤーも遠くから確認できますので、スナイパーをするならオススメです。ただしこれを装着した状態でフラッシュグレネードの影響を受けるととても長い間視力を奪われます。

Backpack Medkit

これは他の人に対して治療をするときに意味のあるものです。通常治療をしても最大40パーセントまでしか体力は回復しません。しかしこれをもっている場合は80パーセントまで回復させることができます。ただし自分自身には無効なのでご注意ください。

投稿者 ikanatto : 11:40 PM | コメント (11)

April 20, 2005

ET 2.60 patch Readme 和訳

すっかり旬を逃しましたが2.60 Readmeの和訳です。
和訳したものの意味のわからないものが私自身たくさんあったので???な部分があるかもしれませんが、もし発見なされた場合はコメントなどでご指摘いただければ幸いです。

Ver 2.60 雑感

しかし2.60になって随分と色々と変わった感じがしますね。当たり判定あたりがかなり変わった気がするのは私だけでしょうか!?バグフィックス関連は概ねよい感じですが、まだ火炎放射器が奇天烈な当たり判定を発生させるあたりはどうにもなっていないようです。残念。

あと実用性に乏しいと個人的に思うフィーチャーがいくつか追加されています。異方性シェーダーなんて使う機会あるんでしょうかね。例外としてHTTPリダイレクトさせるときにエージェント情報を付与するようになったのはよいのではないでしょうか。

なんにしても、他MODが2.6に対応するのが待ち遠しいですね。ETPROはパッチ自体に関わっていたのですぐ対応できるものだとばかり・・・。

2.60 Fixes:

新規追加事項

サーバーとクライアントに低速CPU向けの若干最適化をした。
クライアントはゲームをwav保存可能になった。
wav_recordで音声レコーディング開始。
wav_stoprecordで停止。
cl_wavefilerecordを1にすると、デモ再生開始から少し後にレコーディングが始まる。
"Unable to load an official pak file"のメッセージはどのファイルがないのかを表示するようになった。
Win32とリナックスでサウンド関連cvarが統一された。:
s_khz, s_bits, s_numchannels
s_khzはCVAR_LATCHに変更。
プレイヤーはcom_ignorecrash 1にすることでクラッシュするような危険なcvar設定を上書きすることが出来る。
"server is full"の代わりにsv_fullmsgで別のメッセージを表示させることが出来る。
サーバーは接続数が一杯の時に新規接続プレイヤーを別のサーバーにリダイレクトできる。sv_fullmsg "ET://host.to.redirect.to:portを設定。
mapコマンドでキャンペーンを開始できなかった時、サーバーはGT_WOLFを読み込む。
デモ再生コマンドはdm_83拡張子を明確に判別するようになった。拡張子が指定されない場合、dm_83かdm_84として扱うことを試行します。
接続中のプレイヤーには簡略されたスナップショットを送信することで通信帯域を節約するようになった。
クライアントはデモをレコーディング中、cg_recording_statuslineを設定することでレコーディングステータスの表示位置を変えられるようになった。0にすると非表示となる。
/buyNowと/singlePlayLinkは削除。
メニューにMODSを追加。
異方性テクスチャーフィルタリングが描画でサポートされた。r_ext_texture_filter_anisotropic 1で有効。r_textureAnisotropyでどの程度処理するかを設定。
cg_draw2Dはcheat protectedではなくなった。
腹筋状態のときに周囲を見ることが出来るようになった。また処理を完全にクライアントサイドに移したのでラグがなくなった。
投票関連についてもコンソールログに流れるようになった。

*win32のみ:
クライアントは/clearviewlogで専用コンソールログをクリアできる。
s_khz 44が正常に使えるようになった。

*Linuxのみ:
dgamouse 2に設定することでクライアントはOS・アプリケーションのマウス加速機能を無効に出来る。
r_swapIntervalを使用可能になった。
キーの割り当てをLinux向けに調整。CTRL/SHIFT/ALTなど、正常に割り当てられている。

バグ修正関連

クラスなど:
腹筋状態のCoが変装可能を修正。
Lv4のCoが腹筋中の相手に不適切なナイフダメージを与えていたのを修正。
変装Coの体力が相手チームに表示される時にラグがあったのを修正。
Lv3のエンジニアが地雷に使うチャージを修正。
FoがAmmo Packを出すのにMedのチャージを消費をしていたことを修正。

その他一般:

HTTPダウンロードでまれにクラッシュする不具合を修正。
ダウンロード時にクライアントはID_DOWNLOAD/2.0 libcurl/7.12.2というUserAgent情報を送信する。またリファラーとしてET://server.IP.address:portを付加する。
サーバーエンジンの各種クラッシュ不具合を修正。
スペクテイターが接続時に時々壁をすり抜けてしまう不具合を修正。
profile.pidの問題を修正。
1キャンペーン中に10マップ以上含まれる時、クラッシュする不具合を修正。
生きているプレイヤーでもスコアにどくろが表示されている問題を修正。
キャンペーンごとにキャンペーン数がカウントされている問題を修正。
cg_drawgun 0にすると車載銃の銃口原点位置が正しくなかった問題を修正。また火炎放射器と車載銃を同時に発射できる問題も修正。
Light WeaponとHeavy WeaponがLv4になるとクラスがSoldierになる問題を修正。
レフリーメニューの色を二度拡張する不具合を修正。
海外のキーボード(英語圏以外)でコンソールを開いた状態でサポートしていない文字を打ち込める問題を修正。
cl_languageがー1か0のときに翻訳が不適切だった問題を修正。
何かを拾った時(Radar Partsなど)、HUDに表示されるまでラグがあった問題を修正。
地雷がときどき作動しても無音だった問題を修正。
Bindした内容が全てLowercastされる(先頭単語がコマンドの場合問答無用で実行される)問題を修正。
蘇生するとプレイヤーの視線が回転する問題を修正。
Players_Axis/Players_Alliesが大きくなりすぎてサーバーをクラッシュさせる問題を修正。
プレイヤー同士がぶつかって・重なってスタックする問題を修正。
動くものの上にダイナマイトを設置してオブジェクティブが作動する問題を修正。
"configstring > max_configstrings"で接続できない問題を修正。
スペクテイター中にサッチェル爆弾が正常に作動しない問題を修正。
作成が完了した建造物での危険予測メッセージ表示を修正。また墜落時も修正。

主にバランス調整など:

アンチバニーホップを修正。
イベントが同時に起こる問題を修正。
ピストルが早く撃てる問題を修正。
MG42発射速度のフレームレート依存性を修正。
マップ最下点に平らな面がある場合トレースマップが壊れる問題を修正。
Lv4能力を獲得した時ランダムでスペクテイターに変わってしまう問題を修正。
Antilagを修正。
ログでタイムスタンプが一部削除されている問題を修正。
レンダリング時伸張画像が多すぎるとクラッシュする問題を修正。
特定の地形で飛ぶと沈んでゆきやがて墜落する問題を修正。
特定のIPとLANアドレスを誤って解釈する問題を修正。
高次アスキー文字が誤った色で表示される拡張書式の問題を修正。
砲撃マーカーがコンパス上で正常に表示されなかった問題を修正。
9文字目にピリオドを含み全体の長さが21字のホストがIPXとして扱われていた問題を修正。
MAX_GLOBAL_SERVERS以上がマスターサーバーから戻ってきたときにオーバーフローしていた問題を修正。
ルガーの散弾率と最後の弾を撃ったときの硬直を修正。
二丁拳銃が早く撃てる問題を修正。
狙撃のズームを修正。
クライアントがIPを上書きできるセキュリティホールを修正。
スペクテイターとリンボーカメラがゆっくりと滑ってとまらない問題を修正。
サーバーコンソールにリダイレクト通知が何度も表示されることを修正。
LMSにてFIRST BLOODが表示されなかったのを修正。
LMSにてチームが何勝しているのかを表示しなかった問題を修正。
投票が行われている最中に接続してきたプレイヤーがマップ終わるまで投票できない問題を修正。
Fuel Dumpにて"base fortification"と表示される場所にダイナマイトを設置してもFuel Dumpが破壊できる問題を修正。
pmoveと武器チャージに関する不適当な部分を修正。
Radarでダイナマイト解除に関するバグを修正。
チームの地雷が全て使われているときに相手チームの地雷を解除できなかったバグを修正。
地雷のカウントが正しくされなかったバグを修正。
Goldrushでダイナマイト設置アナウンスと実際が合致しない問題を修正。
プレイヤーが建設可能なオブジェクトを破壊するためのダイナマイトを阻害する問題を修正。
プレイヤーの伏せ状態がパケットロスによって失われる問題を修正。
3秒以内に相手を全滅させ、かつ自分らが自殺した場合に勝利したチームが勝利とならなかった問題を修正。
LMSにて一方のチームが全滅しても終わらなかった問題を修正。
ポーズ中での武器持ち替えを修正。
二丁拳銃とMG42がcg_autoreloadを無視していた問題を修正。
timelimit 0におけるMaxlives調整を修正。
照準に表示される名前がたまに正しくなかった問題を修正。
g_{axies,allies}mapxpが長いキャンペーンにてオーバフローを起こしサーバーをクラッシュさせていた問題を修正。
固定されたMG42がフェンスなどの障害物を破壊しない問題を修正。
プレイヤーがディスコネクトした時に'complaint dismissed'と表示されていた問題を修正。
MG42がアンチラグ処理されていなかった問題を修正。
ライフルグレネードがチームドアを貫通していた問題を修正。
インターミッションにおけるReady処理をmatch_readypercentに変更。
インターミッションのReadyはスペクテイターのものを含まないようになった。
過剰なcg_errordecayが出力される問題を修正。
Coの地雷発見に関する問題を修正。
ナイフがアンチラグ処理されていなかった問題を修正。
破壊できるオブジェクト越しに伏せた時、プレイヤーが自身の頭部を撃ってしまう問題を修正。
腹筋時に時々Med以外のクラスでも視線が固定されてしまう問題を修正。
大きすぎるサーバーコマンドがクライアントをクラッシュさせていた問題を修正。
サーバーコマンドラインにて//が処理されていた問題を修正。
ガーランドとK43の弾薬放出について修正。
スペクテイターになった時に弾薬が配られていた問題を修正。
リンボー中のプレイヤーがキャリア中のプレイヤーを見ているときにディスコネクトするとサーバーが落ちていた問題を修正。
攻撃距離によるダメージ減少の不具合を修正。
setu chがサーバーをクラッシュさせる問題を修正。

伏せているプレイヤーが壁にめり込む問題を修正。
Ctrl-`でミニコンソールが開いていた問題を修正。
スペクテイター時、変装しているプレイヤーの元の名前と服を奪った相手の名前が出ていた問題を修正。
名前が32文字以上のプレイヤーに対するkick/mute/referee/etcが出来なかった問題を修正。
オーバーヒートした武器が時々最大値を超えると熱が初期化されていた問題を修正。
血や破片が生じた時にまれに動けなくなっていた問題を修正。

Batteryにおいて、bunkerの旗をとると自動的にスパウンポイントが移動するようになりました。
Railgunの貨物車のような移動オブジェクトは停止よりも継続移動することを優先させます。
クライアントは/userinfoが使えなくなり、これによりuserinfo nukeも出来なくなります。
MG42とモーターを構えたときに双眼鏡は使えなくなりました。

インターミッション寸前に死んでも"Killed by "が表示されなくなりました。
未使用cvarのcg_specswingが削除されました。

F13〜F15のキーも使えるようになりました。
LinuxでもCapslockとkp_numlockとkp_equalsが使えるようになりました。

Made some minor fixes to Linux keyhandling.
その他Linuxでのキー入力に関するマイナーな修正を行いました。

投稿者 ikanatto : 01:28 AM

April 18, 2005

Wolfenstein:Enemy Territory関連のファイル

Files

作ったものなどです。出来があまりよろしくないのは初心者ということでご勘弁ください。

現在ファイルの配布は諸事情により見合わせています。

Several files were removed because of some reasons.

zipやrarはそのまま解凍してください。pk3ファイルが出てきたら、etmainかご希望のMODフォルダに入れてくださればそれで導入はおしまいです。飽きた場合には移したpk3ファイルを削除することでなかったことにできます。

ETアニメスキン

ファイル

スクリーンショット

スクリーンショット見ていただければ説明不要でしょうか。思いっきり趣味なので気持ち悪いと感じた方は無理に導入することはないかと存じます。あとキャラクターごとに作りの雑さが違うのは、作った人が違うためです。より雑できたない出来のものがikanatto作、そうでないものはとある知り合いのものです。

事情によりファイルは削除しました。

Wolfenstein:Enemy Territory Custom Model Depot Yardのアーカイブ群

ファイル

昔作った文書をまとめてzipしたものです。ちなみにzipした文書は現weblogに大半は移してあるので改めて必要になることはないかもしれません。

W:ET プレイヤーモデルアニメーション

ムービー

ETで使われているプレイヤーモデルのアニメーションを連続再生したものです。ちなみにこれだけでは使いようがないので、ふーん、こんなのもあるんだー程度に見てやってください。

W:ET プレイヤーモデルアニメーション 3ds maxデータ

ファイル(rar) 

上記アニメーションの3ds maxデータです。私の3ds max環境は5.1Jにcharactor studio 3.0です。メッシュデータとしてAxisのCovert Opsが使われています。リクエストあれば他のメッシュデータにも対応できますのでご相談ください。

投稿者 ikanatto : 06:34 PM | コメント (383)

April 17, 2005

ご意見・ご感想

Movable Typeは掲示板のアドインがないようです。ということで、ご意見などございましたらこちらの文書にコメントとしてご投稿ください。

投稿者 ikanatto : 03:51 AM | コメント (1191)

What's Urban Terror

私の大好きなゲームの一つQ3AのMOD、Urban Terrorについての紹介です。

もうちょっとでETにも移植される様子なので、折角の機会とということで紹介文を作ってみます。

Urban Terrorとは

Urban Terrorは、Sillicon Ice development(MOD作成有志)によって作られたQ3AのMODの一つです。テーマとしてはご存知Counter Strikeに近いもので、近代を舞台にテロリストと特殊部隊による銃撃戦です。

プレイ感覚としては、ダメージシステムがリアル系、運動システムがスポーツ系となっており他のMODとは一味違った味付けです。私個人としては、CSよりもこちらの方が好きです。

Quake2にAction Quake2というMODがあったのをご存知の方がいらっしゃるかもしれませんが、まさしくそれの後継者といった感じです。

Urban Terrorの概要

登場する武器は基本的に実在のもので、頭部への被弾は即死、それ以外でも大抵重症となります。脚部へのダメージが運動能力の低下、腕へは照準のぶれなど、実に凝っています。また武器以外にも装備品を選ぶことが可能で、防弾チョッキ、ヘルメット、追加弾薬、ナイトビジョン、手榴弾、閃光弾、煙管、サイレンサー、レーザーサイトなどが選択可能です。

ゲームモードはデスマッチ(FFA)、チームデスマッチ、CTFに加えてETでいうLMSに相当するSurvivorがあります。どのゲームモードでも楽しく遊ぶことが出来ます。特にサバイバーモードは連携がものをいうクラン戦向けのモードですから、ライトユーザーからヘビープレイヤーにまで対応できます。

Ver3以降からBombモードが追加されています。これはCounter Strikeにあるような、ダイナマイトをマップ規定箇所に設置し、これを防衛または解除を巡って争うものです。なかなかにアツイです。

また登場してからそこそこ年月が経過しており、ユーザーやオフィシャルによるカスタムマップやスキンが流通していることも見逃せません。いくらでも遊ぶことが出来ます。

Urban Terrorの導入方法

まず始めにQuake 3 Arenaを持っていない人は買ってください。今となってはとても安価に買うことが出来ると思います。そしてQ3Aの最新パッチを当ててください。1.32あたりが最新でしょうか。

そして、Urban Terrorサイトにて最新のUrban Terrorのファイルを手に入れてください。恐らく3.7がQ3A版最新バージョンでしょう。

ダウンロードしたファイルを解凍して出てきたpk3群をQ3Aフォルダ以下に置けば基本的に準備は完了です。より詳細な導入方法やマップなどの手に入れ方はETUTが出てからまた書きたいと思います。

投稿者 ikanatto : 03:08 AM | コメント (940)

April 16, 2005

W:ETソースコード朗読会 第六回

Reading the Source Code of W:ET #6

今回のねらい

何だかんだで六回目になりました。ETも久しぶりのバージョンアップを受け、2.60になりました。これに伴い、ソースコードも新しいものが配布されています。今回からはこちらを扱います。ダメージ減衰に関するコード修正その他いろいろフィックスがなされているようです。

今回はアンチラグ機能がどのように働いているのか、についてみていきましょう。

アンチラグって?

アンチラグは何するものぞ、と思われる方がいらっしゃるかもしれません。ラグとはご存知の通り、クライアントサーバー間での通信処理によって生ずる同期のずれ、タイムラグのことです。そのため、クライアント側ではきちんとターゲットに対して命中する方向でも、サーバー上のデータでは既にターゲットはそこにはおらず命中しない現象が起きます。そのため、Pingの大きなサーバーでは「先読み」をして攻撃を行うことがいわばFPSの常識になっていました。

これを解決するためにアンチラグが導入されています。つまり、クライアントの動作内容と行った時間をサーバーが考慮して、クライアントが実際に見ている条件下にて当たり判定を行うものです。このため、クライアントは先読みをする必要はなくなります。

しかしまったく問題がないわけではありません。Pingの小さい人と大きな人が出会い頭にパンツァーファウストを撃ったとします。結果、Pingの小さい人が大きな人を「必ず」先制します。これは通信反応速度の差であり、アンチラグ機能をもってしてもこれを埋めることはできないのです。

アンチラグ機能実現のためのコード群が入っているのはg_antilag.cが主です。Ver2.60になってからナイフとMG42に関してもアンチラグ機能が働くようになりました。つまり、当たり判定の際にはここの関数が呼び出されていることになります。さっそくみていきましょう。

アンチラグ:位置情報保存

void G_StoreClientPosition( gentity_t* ent ) {
	int top, currentTime;
	
	if (!(	ent->inuse && 
 			(ent->client->sess.sessionTeam == TEAM_AXIS || ent->client->sess.sessionTeam == TEAM_ALLIES) && 
 			ent->r.linked &&
 			(ent->health > 0) &&
 			!(ent->client->ps.pm_flags & PMF_LIMBO) &&
			(ent->client->ps.pm_type == PM_NORMAL)
 	)) {
		return;
	}

	(省略)

	ent->client->clientMarkers[top].servertime =	level.time;
	ent->client->clientMarkers[top].time =			currentTime;
}

一番最初の関数です。G_StoreClientPositionという名前からして、クライアントの位置情報を保存しておくもののようですね。詳しく見ていきましょう。

	top = ent->client->topMarker;

	// new frame, mark the old marker's time as the end of the last frame
	if( ent->client->clientMarkers[top].time < level.time) {
		ent->client->clientMarkers[top].time = level.previousTime;
		top = ent->client->topMarker = ent->client->topMarker == MAX_CLIENT_MARKERS - 1 ? 0 : ent->client->topMarker + 1;
	}

topにtopMarkerを代入しています。つまり、topはサーバーに記録されている最新のクライアント位置情報の「番号」です。

次のif条件は、記録されているクライアントの時間がサーバーの時間よりも小さいかどうかをみています。もしそうなら、この記録時間をpreviousTimeに置き換え、topを1個増やす、つまり新しい記録番号にします。


	VectorCopy( ent->r.mins,						ent->client->clientMarkers[top].mins );
	VectorCopy( ent->r.maxs,						ent->client->clientMarkers[top].maxs );
	VectorCopy( ent->r.currentOrigin,				ent->client->clientMarkers[top].origin );
	ent->client->clientMarkers[top].servertime =	level.time;
	ent->client->clientMarkers[top].time =			currentTime;

ここでは新しくクライアントの位置情報と時間を記録しています。

サーバーではクライアントの「位置」と「時間」がセットで記録されていることを憶えておいてくださいね。

アンチラグ:Historical Trace

クライアントの位置情報が時間とともに保存されていることを確認しました。それでは実際の当たり判定を見ていきましょう。これはG_HistoricalTrace関数で行われています。

	if( !g_antilag.integer || !ent->client) {
		G_AttachBodyParts( ent ) ;

		trap_Trace( results, start, mins, maxs, end, passEntityNum, contentmask );

		res = G_SwitchBodyPartEntity(&g_entities[results->entityNum]);
		POSITION_READJUST

		G_DettachBodyParts();
		return;
	}

ここはアンチラグ機能が無効、または対象がクライアントでない場合の処理です。いわゆる普通の当たり判定を行っています。ちなみにG_AttachBodyPartsとG_DettachBodyPartsはクライアントにヘッドや足などの個別の当たり判定箇所をくっつけたり取り除いたりします。

	G_AdjustClientPositions( ent, ent->client->pers.cmd.serverTime, qtrue );

	G_AttachBodyParts( ent ) ;

	trap_Trace( results, start, mins, maxs, end, passEntityNum, contentmask );

	res = G_SwitchBodyPartEntity(&g_entities[results->entityNum]);
	POSITION_READJUST

	G_DettachBodyParts();

そしてこちらがアンチラグ機能が有効な場合の当たり判定です。まずはG_AdjustClientPositionsから見ていきましょう。二つ目の引数に、ent->client->pers.cmd.serverTimeをエントリーしている点を憶えておいてください。この数値は、クライアントが最後に受け取ったデータのタイムスタンプです。つまり、クライアントが今実際に見ている状況を再現するサーバー時間です。

void G_AdjustClientPositions( gentity_t* ent, int time, qboolean forward) {
	int			i;
	gentity_t	*list;

	for ( i = 0; i < level.numConnectedClients; i++, list++) {
		list = g_entities + level.sortedClients[i];
		// Gordon: ok lets test everything under the sun
 		if (	list->client && 
 				list->inuse && 
 				(list->client->sess.sessionTeam == TEAM_AXIS || list->client->sess.sessionTeam == TEAM_ALLIES) && 
 				(list != ent) &&
 				list->r.linked &&
 				(list->health > 0) &&
 				!(list->client->ps.pm_flags & PMF_LIMBO) &&
				(list->client->ps.pm_type == PM_NORMAL)
 		) {
			if(forward) {
				G_AdjustSingleClientPosition( list, time );
			} else {
				G_ReAdjustSingleClientPosition( list );
			}
		}
	}
}

大きなif条件がありますが、まとめると対象がクライアントでチームに参加していて攻撃対象と攻撃者が同じではなくてヘルスが1以上であることをみています。もしそうなら、G_AdjustSingleClientPositionによって攻撃対象(条件を満たすすべてのクライアント)の位置情報を攻撃しているクライアントのtime(ent->client->pers.cmd.serverTime)によって修正します。実際のコードを見ていきましょう。

static void G_AdjustSingleClientPosition( gentity_t* ent, int time ) {
	int i, j;

	if( time > level.time) {
		time = level.time;
	} // no lerping forward....

	i = j = ent->client->topMarker;
	do {
		if(ent->client->clientMarkers[i].time <= time) {
			break;
		}

		j = i;
		i = (i > 0) ? i - 1 : MAX_CLIENT_MARKERS - 1;
	} while(i != ent->client->topMarker);

	if(i == j) { // oops, no valid stored markers
		return;
	}

最初のif文は、クライアントがサーバーよりも先のフレームに達していた場合これをサーバーの時間に同期させるためのものです(こんな状況はローカル接続でもしない限りなかなか起こりえませんが・・・)。巷ではcom_maxfpsを操作することによって当たりやすくなるなどの噂が囁かれています。実際にfps設定値はこのあたりでフレームごとの時間という形で効いてきます。つまり、フレームごとの時間が短いほど位置情報が詳しく記録されるため、当たり判定が細かく計算されます。このため当たり易さに差が生じる可能性があるのです。

次のwhile構文がまさにアンチラグ機能そのものです。修正対象となるクライアントの位置情報が登録されている時間と攻撃者が見ている時間を比較して、同じか前者が後者よりも前になったときの位置登録番号を求めています。

これによって「攻撃者視点から」のタイムラグを打ち消しストレスのないゲームプレイを実現しています。ただし、「被攻撃者」からは自身が攻撃を受けるタイミングではないはずの時にもダメージを被る可能性があります。これは攻撃者のPingが悪ければ悪いほど生じやすくなるので、どこまでこの機能を有効にするかは実に考えどころです。

次に参りましょう。

	if(i != ent->client->topMarker) {
		float frac = ((float)(ent->client->clientMarkers[j].time - time)) / (ent->client->clientMarkers[j].time - ent->client->clientMarkers[i].time);

		LerpPosition(ent->client->clientMarkers[j].origin,		ent->client->clientMarkers[i].origin,	frac,	ent->r.currentOrigin);
		LerpPosition(ent->client->clientMarkers[j].mins,		ent->client->clientMarkers[i].mins,		frac,	ent->r.mins);
		LerpPosition(ent->client->clientMarkers[j].maxs,		ent->client->clientMarkers[i].maxs,		frac,	ent->r.maxs);
	} else {
		VectorCopy( ent->client->clientMarkers[j].origin,		ent->r.currentOrigin );
		VectorCopy( ent->client->clientMarkers[j].mins,			ent->r.mins );
		VectorCopy( ent->client->clientMarkers[j].maxs,			ent->r.maxs );
	}

ここは、フレームとフレームの間の「時間的な」隙間を補完するための部分です。例えば攻撃者が行動した時間が3秒めだっととします。ところがサーバーに保存されているクライアントの位置情報が1秒と5秒のものしかない場合があります。そんな時、3秒目の位置情報を推測するために、直前と直後の位置をクライアントが直線的に動いたと仮定し、攻撃者の行動時間での位置を計算しています。

当然ながらここにも誤差を含む可能性はありますが、大抵の場合は良好な結果となります。こういった部分を見ても、実戦さながらのジグザグな動きが相手の攻撃を回避する上でとても有効であることがわかります。

さて、話をG_HistoricalTraceに戻します。G_AdjustSingleClientPositionによってクライアントの位置を修正もしくは補完し、そしてtrap_Traceにより当たり判定を見ています。この結果はresultsに格納されます。そして過去の状態になっている各クライアントの位置情報を、G_AdjustClientPositionsを3つ目の引数をfalseで呼び出すことで元に戻しています。

ちなみにG_SwitchBodyPartEntityは当たった体の部位に応じて処理を分ける関数のようですが、現在のところ目だった機能は果たしていません。頭でも胴体でも足でも「クライアントに当たった」としています。

これ以降のコードは、位置修正してクライアントにヒットしたとき位置修正なしでの当たり判定を行い、この結果をresに代入しています。

まとめ

今回はアンチラグ機能ということで、サーバーとクライアント間での時間差を考慮するコードを見てきました。なかなか興味深い部分でしたね。クライアント間の環境差は仕方のないことといえばそれまでですが、コードでこれを公平に補うことが出来るなら素晴らしいことだと思います。クライアントが個人を益するために補正をかけることはチート行為です。やめましょうね。

投稿者 ikanatto : 06:14 PM | コメント (1269)

April 15, 2005

Trouble Shootings for Modeling

"OMG! WHAT THE HELL IS THIS!?"

I can not remember how often I have said the above words. Yes, still saying that. But the activity of the modeling for a half of year, I have earned some know-hows.

If you are a new challanger of modeling, my knowledgements may help you. I hope so, hope so.

Troubles in ET

Checking the console log first!

Generally if errors occured while loading model data, console log displays those.


In this situation, ET failed to find the image file for accessory and body because of missing of skin file and path to non-existing files.

The console log is extremely useful for us to shoot the bugs. If you have problems in game, check console log first.

Black and yellow texture appears instead of your image file.


This means that ET failed to find the shader to object(s). The model data holds its shader info in it, but almost models has nothing about it.

So we need to define their shader by using the skin file. Check your definition file has proper path or name.

The skin filename must be modelname_classname.skin if your player model name is modelname.mdm.

For other models, like helmets or accesseries, the md3 also needs the skin files. If model name is modelname.md3, the skin file must be modelname.skin.

The model does not appear.


Looks like no errors but your model does not be seen, its reason can be that skin file exists correctly but defines the non-existing image file.

Check the console log and the skin file descriptions.

One more thing you must remember that the image width and height to be used as texture must be the numbers powered by 2. e.g. 512*512 or 128*256 are OK but 384*512 and 64*768 are NG.

The XYZ orientations appear instead of your model.


This will occur when the model file is not found. Check your definition files such as .char or .skin.

ET crashes when the model you made should be rendered.

This will occur when your model data has invalid value. e.g. too large bounding box otherwords too far vertices, negative value which must be positive( bone length, index number as well ). These invalid values are almost generated when the model data is converted, also may be done by my mdstool.

There are a little ways to correct this. Preventing the models from converting will do but you can not use the model in wanted format.

The player model looks floating or sinking!

This is caused if the mdx information is not compatible for your player model. The default mdx is best for the models which are similar to the default models. Their height is 80 in max unit. You can correct this by modification of mdx. My mdstool supports mdx adjustment and rescaling. But other problem will happen with mdx modification. Using the different mdx costs more memory. Thus your player model can be hard to use for low power PC users.

WTF!? My player model is collapsed but it looked well before exporting to mds!!

Be easy first. This can be caused if you rescaled the models which had been already physiqued with the biped.

I think this it the 3ds max bug or problem of the export-skelton plugin. So you should adjust your model size before physique. My mdstool supports to rescale mdm and mdx though I can not recommand this way because the rescaled model may look something weird...

Troubles in Exporting

When you export skl from max, max says that error occured and closes.

The indicator of progress goes for some point and but max closes... This can be caused if your player model has too many vertices.

I do not know the precise number of max vertices, though models with over 2000 vertices always fails to be exported, a 1500 vertices model can be exported.

So make sure that your model has lower vertex(or poly?) counts as possible as it can. Of course lower poly design tech required.

In max, the player model has several objects, but exported model has only 1 object.

This is caused if you forgot to assign the unique material number to each object. The sklout plugin regards the obejects which has the same material number as one united object. Thus although the objects has the same material, the material number must be different each other.

e.g. u_body assigned 1, u_lfthand 2, u_rthand 3, l_legs 4.

Some md3 exporters of max have issue.

This is not a common problem, but for the some max plugins which enable to export md3.

The plugins which do not require the setting of texture file path will implant the texture file path which is currently used in md3.

If md3 is loaded in ET, the texture file must be in /etmain/ subdirectories, so path must be such as "models/players/temperate/somemodel/". Although if you work on the different folder structure, the texture file path will not point the correct path with those plugins.

This can be corrected with using the skin file. However error message remains in console, but the texture applied by refering the skin file.

How can I handle MDC?

MDC is a model data like md3 but compressed. Its benefit is saving some memory, however, not much memory.

A man of SD said that you should not bother with mdc, just use md3. mdc is slightly difficult to use than md3 because not so popular.

Rather than using mdc, md3 prefered clearly.

投稿者 ikanatto : 07:26 PM | コメント (228)

Adding New Animations

This tutorial spots how we can add the new animations to the existing player model system. I will explain that how we add the new animations to the deafault bone system, but you should be able to do this in another bone system.

Quick tutorial for adding the new animations


1. Create animations with biped in max

Well, the first step to climb up is creating animations. To do this, you must create the default biped. For more detail about the default biped, here. This work is pretty fun for me :). If you are doing with the Character studio plugin, it will help you to create smart animations easily. When it is done, write down somewhere about "Name of animation", "First frame number", "Length", "FPS to play this animation" for each animation you got.

You may want to know the default biped figure. Yes I wanted to know that. So I have adjusted the biped to fit the normal player character. You can get it here( figure file ).

2. Skin up the biped roughly.

As for the mds exporting plugin can not reguard the biped mesh itself as the model object, we need to make a skin around the biped. No need to make a detail skin, just dummy. Physique the model with biped roughly, but remember each bone must have a link to 1 vertex at least.

Guess what is the most simple skin that fullfill that state...? Yes, it can be an object with one triangle. Attaching this object to the biped node and assign the vertices to all bones. Whoa, you have made it. Easy?

3. Export mds and cut into mdx

Now we are going to export mds so that we can get the animation data into ET. You can cut mds into mdx by my tool with running mode 2.

4. Configure the .aninc file

What is the .aninc file...? The aninc file is the definition of the mdx to tell ET what animations this mdx holds and those properties, so that ET can handle the series of frames as an animation to play.

The aninc file format is the followings. Each animation requires this description.
<Animation Name> <First Frame Number> <Length of Animation> <Looping> <FPS to play> <Move speed> <Transition> <Revered>

For example, suppose that a mdx holds the 3 types of animation, punch, kick, guard. The punch animation ranges from 0 to 14 frames and it should be played at 30 fps. The kick animation is 15-44 frames at 30 fps and the guard animation is 45-59 frames at 45 fps. The animc for this mdx is the followings:

// animation config for the custom mdx
	punch	0	15	0	30	0	0	0
	kick	15	30	0	30	0	0	0
	guard	45	15	0	45	0	0	0

Save your aninc text as animationname.animc. animationname prefers to be the same as the mdx name animationname.mdx.

5. Put the mdx and aninc into the folder tree

Maybe you already have, if you do not then just make, the folder animations/base/human somewhere on your disk. Put the your mdx and animc file into animations/base/human/.

6. Configure the anim file to include your mdx

Now let's copy the human_base.anim at animations/ to your tree and open it with text editor. Then, add the lines to the line before the last '}' like this:

animgroup
{
	animfile "animations/human/base/body.mdx"
	{
		#include "animations/human/base/body.aninc"
	}
	animfile "animations/human/base/mortar.mdx"
	{
		#include "animations/human/base/mortar.aninc"
	}
	animfile "animations/human/base/akimbo.mdx"
	{
		#include "animations/human/base/akimbo.aninc"
	}
	animfile "animations/human/base/prone_pistol.mdx"
	{
		#include "animations/human/base/prone_pistol.aninc"
	}	
	animfile "animations/human/base/prone_rifle.mdx"
	{
		#include "animations/human/base/prone_rifle.aninc"
	}	
	animfile "animations/human/base/prone_dual_pistols.mdx"
	{
		#include "animations/human/base/prone_dual_pistols.aninc"
	}		
	animfile "animations/human/base/prone_mortar.mdx"
	{
		#include "animations/human/base/prone_mortar.aninc"
	}
	animfile "animations/human/base/binocs.mdx"
	{
		#include "animations/human/base/binocs.aninc"
	}
	animfile "animations/human/base/no_juice.mdx"
	{
		#include "animations/human/base/no_juice.aninc"
	}
	animfile "animations/human/base/swim.mdx"
	{
		#include "animations/human/base/swim.aninc"
	}
	animfile "animations/human/base/youranimation.mdx"
	{
		#include "animations/human/base/youranimation.aninc"
	}
}

Save this text. Therefore we have configured the anim file to let the default human animation have your animations included and enable to use them in ET.

7. Assign the animations to the actions

Okay, almost done... the left thing is assigning those animations to the specific actions. Copy into your tree the script at animations/script/ of pak0.pk3 then open in text editor. Oi, indeed oi, huge text file. This is the script for assigning the animations to actions. Find out and replace the existing animation to yours where you want the model to play, or if you are modding totally and adding new actions then add the script by refering the existing. As you can see, the player animation is separated to 2 parts, torso and legs. Due to this feature we can save the time to build animations of whole body parts. Nice SD :)

8. Done!

Congraturations, you have added(replaced) the new animations. If you have added the new another action, you need to code to activate it. I think this work also fun for coders...isn't it? To see your new animations in ET, just compress the folder animations and rename its extension to pk3(recommand to rename it as its name starts with a letter 'q' or alphabetically later), place it to etmain.

投稿者 ikanatto : 07:22 PM | コメント (62)

Making player model using default mdx

Using the deafult mdx is a nice idea to save our time to make original animations. The default mdx holds more than 4500 frames. Can you tolerate to make it all by yourself?

If you intend to use the deafault mdx as your mdm's bones and frames, you must create a biped figure which has the same bone strucure as the default has. The default biped is almost normal except having 5 fingers with 3 links, 1 toe with 1 link.




The default biped figure bone list:
Bip01 Pelvis
Bip01 Spine
Bip01 Spine1
Bip01 Spine2
Bip01 Spine3
Bip01 Neck
Bip01 Head
Bip01 L Clavicle
Bip01 L UpperArm
Bip01 L Forearm
Bip01 L Hand
Bip01 L Finger0
Bip01 L Finger01
Bip01 L Finger02
Bip01 L Finger1
Bip01 L Finger11
Bip01 L Finger12
Bip01 L Finger2
Bip01 L Finger21
Bip01 L Finger22
Bip01 L Finger3
Bip01 L Finger31
Bip01 L Finger32
Bip01 L Finger4
Bip01 L Finger41
Bip01 L Finger42
Bip01 R Clavicle
Bip01 R UpperArm
Bip01 R Forearm
Bip01 R Hand
Bip01 R Finger0
Bip01 R Finger01
Bip01 R Finger02
Bip01 R Finger1
Bip01 R Finger11
Bip01 R Finger12
Bip01 R Finger2
Bip01 R Finger21
Bip01 R Finger22
Bip01 R Finger3
Bip01 R Finger31
Bip01 R Finger32
Bip01 R Finger4
Bip01 R Finger41
Bip01 R Finger42
Bip01 L Thigh
Bip01 L Calf
Bip01 L Foot
Bip01 L Toe0
Bip01 R Thigh
Bip01 R Calf
Bip01 R Foot
Bip01 R Toe0

Total 53 bones

Note that if there is a bone which does not link to any vertex when exporting skl, the sklout plugin will ignore that bone. So to get the same bone structure from the default biped figure, you must assign 1 or more vertices at least to each bone.

1. Make the model with textured.


No special things about this work. Same as the standard low polygon modeling.
One thing you should take care of is that an object can use 1 texture because a mds vertex can only 1 uv coordinate. Otherwords, multi material can not be used. However you can use the shader script to player model for multi mapping or ambient and so on.

2. Create the default biped figure and physique your model.


See above about the default biped.

3. Export mds with one frame that model stands ease.

You can get a good tutorial about 2 and 3 process at http://www.mothermushroom.com/images/miscFiles/mdsExport.html.
Nice work pants :)
If you encounter some problems when exporting, my Trouble Shootings may help.

4. Convert this mds to mdm and mdx.

To do this, run the tool with MODE 3.

5. Replace the bone structure of the default mdx to one of the converted mdx.

To do this, set config to use the converted mdx as reference mdx and the default mdx as source mdx, then run the tool with Mode 5. Now your mdx has an original bone structure and the default mdx's animations. You can get the default mdx in animations/human/base/body.mdx by unpacking pak0.pk3.

6. Build up the folder structure to pack up.

Build tree such as:
[root]
 |
 |-[models]
 |  |-[players]
 |     |-[temperate]
 |        |-[yourmodel]
 |
 |-[characters]
 |  |-[temperate]
 |     |-[allied or axis]
 |
 |-[animations]
    |-[human]
       |-[base]

7. Put your mesh, textures, animation file into tree.

mdm, textures, other accesseries model in models/players/temperate/yourmodel/. Put the adjusted mdx in /animations/human/base/ and rename mdx such as body_yourmodel.mdx.

8. Make a anim file.

Open the text editor and write down like as the followings.
animgroup
{
	animfile "animations/human/base/body_yourmodel.mdx"
	{
		#include "animations/human/base/body.aninc"
	}
	animfile "animations/human/base/mortar.mdx"
	{
		#include "animations/human/base/mortar.aninc"
	}
	animfile "animations/human/base/akimbo.mdx"
	{
		#include "animations/human/base/akimbo.aninc"
	}
	animfile "animations/human/base/prone_pistol.mdx"
	{
		#include "animations/human/base/prone_pistol.aninc"
	}	
	animfile "animations/human/base/prone_rifle.mdx"
	{
		#include "animations/human/base/prone_rifle.aninc"
	}	
	animfile "animations/human/base/prone_dual_pistols.mdx"
	{
		#include "animations/human/base/prone_dual_pistols.aninc"
	}		
	animfile "animations/human/base/prone_mortar.mdx"
	{
		#include "animations/human/base/prone_mortar.aninc"
	}
	animfile "animations/human/base/binocs.mdx"
	{
		#include "animations/human/base/binocs.aninc"
	}
	animfile "animations/human/base/no_juice.mdx"
	{
		#include "animations/human/base/no_juice.aninc"
	}
	animfile "animations/human/base/swim.mdx"
	{
		#include "animations/human/base/swim.aninc"
	}
}
The red descriptions depend on what name you saved the mdx as. Save this text as human_base_yourmodel.anim and put it in /animations/.

9. Make a char file.

Open up the text editor, write down like as the followings.
characterDef
{
	skin			"class"

	mesh			"models/players/temperate/yourmodel/body.mdm"
	animationgroup		"animations/human_base_yourmodel.anim"
	animationscript		"animations/scripts/human_base.script"

	undressedCorpseModel	"models/players/temperate/yourmodel/body.mdm"
	undressedCorpseSkin	"naked.skin"

	hudhead			"models/players/hud/head.md3"
	hudheadanims		"animations/human/heads/base.anim"
	hudheadskin		"models/players/hud/team_class.skin"
}
Where class is soldier, medic, engineer, fieldops or cvops if you will replace the exisiting class model.
mesh refers where is the mdm for this model.
animationgroup refers where is the animation setting file for this model. We have made this file so we should set this path to refer it.
undressedCorpseSkin refers the skin file applied when uniform taken by covert ops.

Save this text as classname.char. classname depends on what class you want use this model for. Place this file at /characters/temperate/allied or axis/.

10. Put the skin files.

The skin files are required when models are wrapped. The skin file format is:
objectname,	path of an image file for this object wrapping
If you have u_body, u_lfthand, u_rthand, l_legs objects in your model and they are using the image files body.tga and legs.tga, the skin file should be the followings:
u_body,		"models/players/temperate/yourmodel/body.tga"
u_lfthand,	"models/players/temperate/yourmodel/body.tga"
u_rthand, 	"models/players/temperate/yourmodel/body.tga"
l_legs,		"models/players/temperate/yourmodel/legs.tga"
You can attach some accesseries such as helmet or backpack with adding lines into the skin file. The followings are quoted from Splash Damage http://www.splashdamage.com/files/docs/playermodels.html.
md3_beltr Attaches to tag tag_bright. 
md3_beltl Attaches to tag tag_bleft. 
md3_belt Attaches to tag tag_ubelt. 
md3_back Attaches to tag tag_back. 
md3_weapon Attaches to tag tag_weapon. 
md3_weapon2 Attaches to tag tag_weapon2. 

md3_hat Attaches to tag_mouth, gets removed when a headshot is given. 
md3_hat2 Attaches to tag tag_mouth. 
md3_hat3 Attaches to tag tag_mouth. 
The skin filename should be modelname_class.skin. e.g. model name is body.mdm and it is for medic, the skin filename should be body_medic.skin. Unlike player models, such as heads or accesseries requires the skin files but its name should be modelname.skin.

11. Finish!


Congraturations, you have made the player model with the default animations. To play with your model, just compress the 3 folders models, animations, characters into 1 zip file and rename its extension to pk3. Place it your etmain folder or mod folder to play with it.


投稿者 ikanatto : 07:18 PM | コメント (13)

unofficial MDM&MDX specification

Here is the data structure of mdm and mdx which I am using in the tool. but there is no guarantee to ensure this document saying truth. So use these info at your own risk.

Player model in ET

Each character used in ET is assigned in characters of pak0.pk3. There are some files to define a character, they refer what class it is, what mesh it uses, what animation group it uses, what mesh it uses when it is undressed, what model its head is.

The mdm is a mesh data file for player models. mdm includes the surfaces and tags. The surface is a geometric data of model or polygon data. Skinning the surfaces with textures, the model appears in ET. The tag is a position info to attach another model to this model. The tag has its position and degree at each frame.

The mdx is a animation data file which contains bone structure and frames. The bone is a skeltal model data. Each bone has its length and its parent bone. The frame is a 3D-position data of the bones. one of the mdx frames have slightly different bone position compared with the previous frame. Thus a series of those frame will make an animation of a action. Ordinarily the mdx has several(or massive) number of animations in it.

A combination of mdm and mdx makes the polygons live, act, be a player.

This system is extremely good for sharing the animations which are used by the similar models. For example, ET only has one mdx( precisely there are more but they are a little ) to be used by the player models, 5 classes of allies and axis team. If we need to create the animation data for each model as like RtCW, The file size will be more bigger and memory requirements will be higher.

However the combination of mdm and mdx is valid when they have the same bone structure. This means that we must create new mdx if we make a non-humanlike model or a small or tall person. I myself think that separating the model data into a set of meshes, tags and bone structure and frames is better if we want to make a small or large human model.

MDM Data Structure

[MDM]
  |
  |--[MDM HEADER]
  |
  |--[Surface]
  |     |
  |     |--[Header]
  |     |--[Vertex]
  |     |--[Triangle]
  |     |--[Collapsemap]
  |     |--[Bone Reference]
  |     :
  |
  |--[Tag]
  |    :
  |
  |--[EOF]


[MDX]
  |
  |--[MDX HEADER]
  |
  |--[Frame]
  |     |
  |     |--[Frame State]
  |     |--[Compressed Bone Frame]
  |     :
  |
  |--[Bone]
  |    :
  |
  |--[EOF]

Detail Data Structure

Here is a data specifications of mdm and mdx. Repeatly I say, they are not guaranteed to be right.

MDM HEADER

MDM HEADER is the header section of mdm. There are information of version, location, Level of Detail, number and offset of the surfaces and tags and so on.
MDM HEADER
NameType and SizeDescription
MDM Identificationchar * 4Magic Number. It should be 'MDMW'.
MDM Versionint32 * 1mdm version. It should be 3.
MDM Namechar * 64Name and virtual path to this mdm.
LoD Biasfloat * 1These are coeffcients of Level of Detail.
LoD Scalefloat * 1
Number of Surfacesint32 * 1Number of surfaces this mdm has.
Offset to Surfacesint32 * 1Offset to the address of the surfaces from this header. Header size is fixed then this value also fixed.
Number of Tagsint32 * 1Number of tags this mdm has.
Offset to Tagsint32 * 1Offset to the address of the tags from this header.
Offset to EoFint32 * 1Offset to EoF of this mdm. Otherwords this mdm file size.

Surface

Surface is a mesh data which will be seen as a part of the player model in ET. Generally a mdm has several surfaces. They are almost separated upper and lower parts, also hands and torso. Example:
MDM SURFACE HEADER
NameType and SizeDescription
Surface Identificationint32 * 1ID or version of surface. It should be 0x00000009.
Surface Namechar * 64Name of this surface. Generally it refers what part it is. Such as u_body, l_legs.
Shader Namechar * 64Name and virtual path of the shader to apply this surface. The ET mdm has nothing here. The skin file defines what shader this surface uses.
Shader Indexint32 * 1?? I dont know what this means. However this is always 0.
LoD Minimumint32 * 1This determines the maximum LoD effect on this surface.
Offset to MDM Headerint32 * 1Offset to MDM Header from this surface address. always negative value.
Number of Verticesint32 * 1Number of vertices this surface has.
Offset to Verticesint32 * 1Offset to the address of the vertices data from the head of surface data.
Number of Trianglesint32 * 1Number of triagles(polygons) which consist of this surface.
Offset to Trianglesint32 * 1Offset to the address of the triangles data from the head of surface.
Offset to Collapsemapint32 * 1Offset to the address of the collapsemap. The collapsemap is the list of triangle indices in order to apply LoD. This list indicates the triangle(or vertex?) will be omitted in that order.
Number of Reference Bonesint32 * 1Number of the bones which are used when this surface will be rendered with calculating its vertices.
Offset to Reference Bonesint32 * 1Offset to the address of the reference bone list.
Offset to Next Surfaceint32 * 1Offset to the next surface or this surface data size.
NameType and SizeDescription

MDM SURAFCE VERTEX is a vertex data of this surface.

MDM SURFACE VERTEX
NameType and SizeDescription
Vertex Normalized Vectorfloat * 3Normalized vector of this vertex. Maybe used for lightening effect.
Texture Coordinatesfloat * 2UV coordinates of the texture image. 3 vertices will cut an triangle image which their potisions are refered by this uv coords from the texture. This value is standardized so ranges from 0 to 1.
Number of Bone Weightsint32 * 1Number of the bone weights which will affect this vertex.

Weight is a element to determine where the vertex is. The vetex potision is calculated by summation of the multiple of the weight and its position of each bone.

MDM SURFACE WEIGHT
NameType and SizeDescription
Bone Indexint32 * 1Index number of the bone which affect the vertex.
Weightfloat * 1A coefficient how much this bone affects the vertex position. A summation of this value must be 1.
Position Vectorfloat * 3The position vector of vertex when this bone is treated as an origin.

Triangle is combinations of 3 vertex indices to buld a polygon.

MDM SURFACE TRIANGLE
NameType and SizeDescription
Vertex Indicesint32 * 33 vertex indices to make a polygon.

Collapsemap data is not specified yet. Reference Bone data is the continuous number of the bone index. They are int32 and their amount is same as the number of Number of Reference Bones in Surface Header.

Tag

Tag is popular for Quake3-Engine-Based game modders. Tag is used to attach another model to this model. mds which has been used in RtCW has different tag data type. mds treat the tags as bones so the position of tag will be changed at each frame.
MDM TAG
NameType and SizeDescription
Tag Namechar * 64Name of this tag.
Rotation Matrixfloat * 9A 3*3 rotation matrix to calculate another model's pose or angles.
Attach Bone Indexint32 * 1Index of the bone which this tag is attached to.
Position Vectorfloat * 3The position vector of tag when attached bone is treated as an origin.
Number of Reference Bonesint32 * 1The number of bones to calculate the tag's parent bone position. Ther are from a root bone to the previous bone of the attached to.
Offset to Reference Bonesint32 * 1The relative offset to the Reference Bones data from the head of this tag data.
Offset to Next Tagint32 * 1Offset to the next tag data or this tag data size.
Reference Bone Listint32 * Number of Reference BonesIndex list of the Reference Bones.

MDX HEADER

MDX Header refers the version, numbers and offset of the frame and bone structure of this mdx.

MDX HEADER
NameType and SizeDescription
MDX Identificationchar * 4ID of mdx, it should be 'MDXW'.
MDX Versionint32 * 1MDX version, should be 2 for now.
MDX Namechar * 4Name and virtual path of this mdx.
Number of Framesint32 * 1Number of the frames this mdx has.
Number of Bonesint32 * 1Number of the bones this mdx has.
Offset to Framesint32 * 1Offset to the address of frames from the header.
Offset to Bonesint32 * 1Offset to the address of bones from the header.
Torso Parent Bone Indexint32 * 1Bone Index to be a torso parent.
Offset to EoFint32 * 1Offset to EoF of mdx otherwords size of this mdx.

MDX FRAME

Frame is a state of the skeltal model at a moment. It can be the scene of firing Panzer, or dying by the head shot, or Nazi salutling. There are 2 sections in Frame, Frame State and Compressed Bone Frame. Frame State refers the bounding box of this model at this frame and a base position which will be used as the parent bone by the root bone. Compressed Bone Frame is the bone position data at this frame. "Compressed" means that each data is not 32bit, but 16bit.

MDX FRAME STATE
NameType and SizeDescription
Bounding Box Vector 1float * 3The relation of 1 and 2 is diagonal. The box area created by using vector 1 and 2 to the box diagonal positions is called Bounding Box. The Bounding Box is used for checking collision or hitting box or trigger entity and so on.
Bounding Box Vector 2float * 3
Local Originfloat *3The origin position which is used at this frame.
Bounding Sphere Radiusfloat * 1Another Bounding area, sphere type. Used for the explosion damage etc.
Parent of Root Bone Tfloat * 3The position used by calculatin of the root bone position.

The Compressed Bone Frame follows after Frame State. It will repeat same times as the number of bones.

MDX COMPRESSED BONE FRAME
NameType and SizeDescription
Angle(Pitch)int16 * 1These are used to calculate the rotation matrix of this bone.
Angle(Yaw)int16 * 1
Angle(Roll)Type and Size
(unused?)int16 * 1Maybe this is inserted to make an unit of Compressed Bone Frame has 32bit order.
Offset Angle(Pitch)int16 * 1This is used to calculate where is this tag.
Offset Angle(Yaw)int16 * 1

To know the bone position, you have to summation the vectors which are calcurated by using ( Parent Distance, 0 , 0 ) as a start vector, rotating it with Offset Angles.

To convert angles to radian, just do this: Radian = 2 * 3.14 * Angle / 65536.0

MDX BONE

Bone Structure is like a puppet. A puppet has joints and fixed-length bones. Bones in ET is so. The meshed are attached around the bones. If a bone moves, mesh moves. If a bone is rotated, mesh is rotated as well.

MDX BONE
NameType and SizeDescription
Bone Namechar * 64Name of this boen.
Index of Parent Boneint32 * 1Index of the parent bone to calculate this bone position.
Torso Weightfloat * 1Coeeficient how much this bone tends to be like torso.
Parent Distancefloat * 1Distance from this bone's parent.
Flag(?)int32 * 1If this bone is a tag, this value is 1 or not, 0. but in ET there is no longer tags as bones, this is always 0.

References and Thanks

http://mojo.gmaxsupport.com/Articles/MDSFileFormat.html

Chris Cookson speced the mds, and this is extremely helpful for ET modder as well. Thanks that great documents.

投稿者 ikanatto : 07:11 PM | コメント (13)

Enemy Territoryで自分だけのモデルを作ろう

はじめての3D体験

折角このサイトに来てくれたのに何も得るものが無かったなんて、こちらとしても申し訳ないので、簡単ながら、1から出発するMD3作成チュートリアルを書いて行こうかと思います。

大仰なことを書きましたが、私もETに触れるまでMOD作る気なんてこれっぽっちもなかった人間です。しかし、およそ半年くらいのほほんと取り組んでいたら、それなりのものが作れるようになりました。その程度のことなのです。別にこれでメシを喰う!というわけではないのだから、その程度の気構えで大丈夫です。

その前に、私の拙い解説よりもよりわかりやすく、またテーマとしても面白い和文チュートリアルがあるので、そちらを参考にするのも良いと思います。

Clan [N]

ナイフをなんとアイスピックにしてしまおうというチュートリアル。こちらはメインツールにMilkshapeを使っています。画像を使った丁寧で簡潔な説明でわかりやすいです。

「うるふぇん」はじめました。

こちらはあろうことか、女性の胸部をヘルメットにしてしまおうというチュートリアルです。その題材について是非は置いといて、メインツールとしてgmaxを利用しています。こちらも画像と丁寧な解説なおかげで問題なく理解できるはずです。

このチュートリアルのテーマ

さて、なにはともあれ、まず自分が作りたいものがあると思います。これを私は「テーマ」と呼んでいますが、この「〜したい」という初期の衝動さえ持続すれば何事も達成可能です。上で紹介したチュートリアルは、「わかりやすいチュートリアル、だけど面白おかしくしたい」というテーマだったのでしょう、きっと。私はあなたの「〜したい」について知る術はありませんが、私はこのチュートリアルによって、あなたが自分で作るモデルはETの中で何になるのか理解し、実践できることをテーマとして作成しています。 要は、何らかのツールで作成した3Dオブジェクトを実際にゲーム中で表示させる方法の紹介です。

私の場合、モデリングに限らず実務上のものや趣味で興味があるけど難しいな・・・と理解の妨げになるのは、決まって専門用語だったりします。私は化学徒なので理科チックな技術的用語はなんとなくわかるのですが、法律や文学に関する方面はさっぱりわかりません。それらを考慮に入れ、このチュートリアルはなるべく用語などを用いず、使っても平易にそれを解説しながら、より多くの人が読めるように進めていきたいと思います。

このチュートリアルで、ああ、モデリングって結構簡単なんだなと思ってくれて、少しでも日本のMOD作成者が増えればと願うばかりです。

MOD3分メイキング

本題に入ります。

今回のように新たにモデルを作ったり置き換えるなど、とにかくゲームの何らかの要素を改変するものをMODと呼びます。MODはModificationの略で改造とかそういう意味です。MODは規模の大小はあれど、ゲームに手を加えるという点ではみんな同じです。特に、大規模で広範囲にわたるMODをTC、Total Conversion、と呼びます。よく知られているCounter StrikeはHalflifeのTCです。

まず、MOD作成のとても簡単な一例をあげます。必要なものは、notepad.exeのようなテキストエディターと、lhasaのようなzipアーカイバーです。

テキストエディターで、以下のように書いたものを、body_soldier.skinとして保存してください:

// This is your first mod to change helmets of allied soldiers to covert ops caps. 

md3_back,		"models/players/temperate/allied/soldier/acc/backpack.md3"
md3_hat,		"models/players/temperate/allied/cap.md3"

l_legs,			"models/players/temperate/allied/leg01.tga"
u_body,			"models/players/temperate/allied/soldier/body.tga"
u_rthand,		"models/players/temperate/allied/soldier/body.tga"
u_lfthand,		"models/players/temperate/allied/soldier/body.tga"

同様に、次の内容をcap_soldier.skinとして保存してください。

helmet,			"models/players/temperate/allied/cvops/cap"

次に、modelsという名前でどこかにフォルダを作ってください。そして、そのmodelsフォルダの中に、playersという名前のフォルダを作ってください。以下、temperatealliedsoldierまで同様にして作ってください。そしてsoldierフォルダの中に、先程保存したbody_soldier.skincap_soldier.skinをそれぞれsoldieralliedに移動してください。最終的に、models/players/temperate/allied/soldier/body_soldier.skinmodels/players/temperate/allied/cap_soldier.skinが得られれば結構です。

続いて、作ったフォルダツリーを、modelsから丸ごと無圧縮でzipにしてください。得られたzipファイルを、まあ適当に、test1.pk3とでも名前を変えて、ETをインストールしたフォルダ中のetmainフォルダに入れてください。

さて、実はこの時点でもう作業は完了しています。では実際にゲームを起動して何が変わったのかを確かめてみましょう。ETを起動して適当にHOST GAMEしてください。ただしDedicatedはOFFで。

AlliesのSoldierのヘルメットが、Covert Opsの帽子に変わっていることに気づきましたか?

では、あなたが今までしたことの意味を説明します。最初に作成した〜.skinというのは、3Dオブジェクトに関する設定ファイルです。今回変更したのは、その中の一行:

md3_hat,	"models/players/temperate/allied/cvops/cap.md3"

この部分だけです。元々はヘルメットのモデルを指定していたこの行を、Covert Opsの帽子に指定しました。続いてフォルダツリーmodels/players/temperate/allied/soldier/を作成しましたが、これはpak0.pk3に現存するファイルを新たに作成した設定ファイルで上書きしたいので、フォルダ位置を同じものに指定するためのものです。そしてzipにしてpk3にしましたが、pk3にすることでET起動時に自動的に読み込まれるようになります。重要な点として、既に読み込まれたpk3中にあるファイルと同名のファイルが後から読み込んだpk3中にあった場合、そのファイルは後から読み込まれたファイルで上書きされます。今回はそれを利用しました。

さて、上記の例でETでのモデルの扱いがなんとなくわかったのではないでしょうか。ETでは、設定ファイル中で指定されているモデルやテクスチャ(後述)を可能な限り利用しようとします。ですから、我々の目標である自分で作ったモデルをゲーム中に登場させるには、設定ファイルを改変し自分の作ったモデルを指定するか、現存するモデル名と同名のファイル名にして上書きするか、このどちらかによって実現可能です。

モデリングツールを手に入れよう

兎にも角にも、まずは3Dオブジェクトをつくらなければ今回の企画は始まりません。3Dオブジェクトとは、私たちがゲーム中で目にする立体的なもの一般を指します。ちなみに3Dオブジェクトを作ることをモデリングといいます。作る人をモデラーといいます。モデリングするにはwindows付属のペイントでは力不足で、それ相応のツールが必要になります。まず、その紹介をしたいと思います。ETは折角のフリーソフトなので、ツールも無料または安価なものが良いでしょう、ということでフリーソフトを中心に紹介します。

gmax *Free*

gmaxはdiscreetが提供するフリーの高性能モデリングツールです。もともと3ds maxというツールが母体となっていて、その機能と保存形式を部分的にしたものがgmaxです。3ds maxは3Dモデリングソフトの分野でかなりのシェアを誇っており、その性能は他の追随を許さないものだそうです。母体が商用でしかも最大シェアのものだけあって、gmaxもフリーツールの中でモデリング能力は群を抜いています。オススメです。ただし、保存できる形式が限られているので、作ったものの目的のフォーマットに出来なかった・・・なんて悲しいオチが待っていることもあるので要注意です。

Milkshape 3D *Share* $25 or EUR 25(ユーロ)

Milkshape 3Dは、ゲームモデラーの間でリーズナブルなモデリングツールとして非常に普及しています。その特徴は、元々ゲームのモデル作りを目的として開発されているので数多くのゲームフォーマットに対応していることです。もちろんMD3もサポートしています。使い勝手としてもなかなか良いと思います。一ヶ月の無料試用期間があるので、その間に用事を済ませてしまえば事実上、「無料」です。

Metasequoia *Free* and *Share* \5000

ゲームのフォーマットはサポートしていないMetasequoia(メタセコイア)を敢えて紹介する理由は、このモデリングソフトが非常に直感的かつ簡単に操作できるからです。また日本語である、という点も初心者の方には見逃せない要素だと思います。とにかく、操作が楽です。視点移動、回転、ズームインアウト、面貼り、UVといった本来なら煩わしい作業が簡単にできます。ただし保存形式でMD3はサポートされていないので、一度標準的なフォーマットで保存してそれを他のソフトで開いて変換する作業が必要になります。

Light Ray 3D *Share* EUR 70

Milkshape 3Dは、最近新たなバージョンへ移行するために対応フォーマット更新作業が滞っている感じがします。そこでこのLight Ray 3Dがなかなかオススメです。Quakeに限らずCall Of Dutyやその他有名ゲームのフォーマットに対応していながら、さらにアニメーション編集も出来るフリーのソフトです。フォーラムのほうでETのプレイヤーモデルフォーマットについての対応も検討していることから、今後の動き次第ではモデリングツールのプライオリティを獲得する可能性もあります。私自身、このソフトを使ったことが無いので操作感などについては述べられませんが、操作画面などを見ると標準的なモデリングツールと大差ないように思えます。特筆事項として、その拡張性の高さがあります。SDKなどのツールが公開されているので、アドオンやスクリプトなどが容易に作成/利用できます。

どのツールを使ったとしても、最終的に作成した3DモデルをETが扱うことの出来るフォーマットであるMD3にしなければなりません。この変換作業は利用しているソフトによってまちまちです。

このチュートリアルは、フリーでゲームモデル、特にQuake3関連のものを作成するのに最適なgmaxを使って説明していきます。

さあ、始めよう!

さあ、モデリングの準備は整いました。でもその前に大事なことを決めなくてはなりません。何を作るか?ということです。本来なら先にそれがあって、作業に移るのですが、今回はチュートリアルですから目的と手段が逆になってしまっています。テーマ設定の良し悪しは、作品の出来だけでなく作業中の自分のモチベーション維持にも大切なものです。

これはチュートリアルなので、簡単なものが良いでしょう。話は変わりますが、メディックは戦場で傷ついた仲間を助ける、看護婦のような、つまりは戦闘服を着た天使のような存在です(安直ですか?)。ということで、今回はメディックを天使様にしてしまいましょう。天使といえば可愛い女の子・・・、というのは日本のアニメ文化に毒され過ぎです。天使といえば、エンジェルハイロウ、そして翼です。そこで、このチュートリアルでは最終的にAlliesのメディックに天使の輪と羽をくっつけることを目標とします。

作るものが決まれば、後はそれを作ってくっつけるだけです。本当にそうなのです。実は何を作るかさえ決まってさえすれば後の障害は技術的なものだけなので、あとは時間さえかければ何とでもなってしまいます。

天使の輪を作ろう

ではでは早速、gmaxを起動してください。面倒なレジストレーションなどを終えると、四分割されたウィンドウやら色々出てきたと思います。まず最初に天使の輪を作りましょう。これはとーーーっても簡単です。なぜ?と思われるかもしれませんが、gmaxはドーナツ状のポリゴンを一操作で作れます。それをクリーム色に着色すれば、ほら天使の輪の出来上がり!それでは実際の作業の流れを追っていきましょう。

まずは起動した状態から、 ↓△肇リックするとドーナツ状のポリゴンを生成するための設定ウィンドウが右下に出ます。この状態になると、中央の作業ウインドウにマウスでドラッグするとドーナツ状のポリゴンが生成されます。それでは作ってみましょう。

,琉銘屬ら△琉銘屬泙悩献疋薀奪阿靴泙后するとドーナツの内径が決定します。続いての位置で左クリックすると外径が決定され、ドーナツが生成されます。そのドーナツは右下のウィンドウで確認できます。ここで各ウィンドウの説明をしておくと、左上はTopとなっておりますが、このウィンドウではモデルを真上から見たものです。右上と左下はそれぞれ正面、左側面から見たものです。右下はパースペクティブ、つまり鳥瞰的にみたものが表示されます。鳥瞰的に表示されると、現実にある遠近法が反映されるため、実際に見た感じになります。

作ったドーナツの形状があなたの感性に合わない場合、このドーナツは削除または修正されるべきです。削除は簡単、このドーナツが選択された状態でDelキーです。修正するには、先程ドーナツを作れる状態にした画面右部にあるところをいじります。

ここの項目をいじると、大きさ以外にも色々と変更できます。実際自分で色々変更してみて、何をどうするとどうなるかを右下のウィンドウなど見ながら確認してください。アンドゥはCtrl+Zです。収拾つかなくなったらDelで削除して新たに作り直しましょう。

さて、満足の行くドーナツ、もとい天使の輪の形状になりましたか?次に、このドーナツに色をつけて天使の輪らしくしましょう。このポリゴンに色や模様をつける作業をスキニングといいます。

天使の輪は円形蛍光灯のようなものっぽいので、今回は暖色のベタ塗り画像を用意しました。皆さんは各自好きな色/模様の画像を用意してもらって構いませんが、注意点として画像の縦横サイズは64/128/256/512ピクセルになるようにしてください。ではこの画像をドーナツに貼り付けましょう。と、その前に作業フォルダでも作ってその中にこのドーナツを保存しておきましょう。保存はCtrl-Sです。今はまだgmax形式保存で結構です。

上図のボタンを押すと、ポリゴンに貼り付ける「マテリアル」を編集するウィンドウが出てきます。マテリアルとは、つまり模様のことです。具体的な話は避けますが、とても奥の深いものです。ここではこれを単に模様を編集するもの、として考えてください。

とりあえず出てきたウィンドウのNewボタンを押して、出てきたウィンドウでStandardを選択してOKをクリックしてください。

ここでは各項目について深く考えずに、Blinnパラメータの中のDiffuse項目の右のボタンを押してください。するとまたウィンドウが出てきますが、このウィンドウで実際に貼り付ける画像を選びます。出てきたウィンドウのBitmapをダブルクリックするとファイルを選択するようなダイアログが出てくるので、あなたが用意した天使の輪画像を指定してください。

指定が終わると、左上に表示されている球に色や模様がついたと思います。これを確認して、 ↓△僚磴妊リックして、一旦このウィンドウを閉じてください。

・・・。まぁ目安としていたのが円形蛍光灯ですから、結果的に蛍光灯になるのは仕方の無いことですね。

それでは作った天使の輪をETで使えるようにするため、大きさや位置の修正、MD3への変換作業に移ります。このあたりはゲームモデル作成固有の作業で、モデリングをしている人が必ずしも知っているわけではないので0から学習するのが難しい部分でもあります。 一旦gmaxでの作業をCtrl-Sで保存して、閉じます。というのも、MD3を扱うためにはgmaxに追加のスクリプトやアドオンが必要だからです。http://mojo.gmaxsupport.com/から、MaxScriptsのQuake3 MD3 ImporterとPluginsのEnhanced MD3 Exporterをダウロードしてください。それぞれ解凍して出てきたファイルを、gmaxをインストールしたフォルダ中のscripts/startup、pluginsに入れてください。終わったら、gmaxを再起動してください。そして先程保存したファイルを開きなおしてください。

2004年7月2日 未完成

投稿者 ikanatto : 07:02 PM | コメント (10)

ダイナマイトで破壊するオブジェクト

Dynamite Object

翻訳元:http://etmapping.andityler.com/dynamiteobject.php

1. お好みのサイズのブラシを作ってください。


2. 作ったブラシが選択されている状態で、2D表示ウィンドウ上のどれでもいいので右クリックを押し、func/func_explosiveを選んでください。


3.ブラシが選択されている状態で、Nを押してentityウィンドウを表示させてください。表示されたウィンドウでは、上部でfunc_explosiveが選択されている状態で、中央部分にはキーとその値が表示されており、下部では変更したい部分と入力するべき値のガイダンスが表示されていると思います。キー入力欄に'scriptname'といれて、Tabを押して値入力欄に移り'dynamitetest'と入力してください。そうしたら、Enterを押してください。これで、このオブジェクトにスクリプトを使うことが出来ます。dynamitetest以外の名前でも構いませんが、変更したら後ほど作る該当するファイル名も変更してくださいね。


***オプション***
4. キーに'mass'、値に'400'を入れて、Enterを押してください。これにより、これが爆破された時により多くの破片が作成されます。またuse shaderを左クリックすると、破片にもテクスチャとシェーダーが適応されます。他にも色々なキーと値があるので、試してみてくださいね。


5. Escを押して選択状態を解除します。別のブラシを、先ほど作ったブラシを覆うように作ってください。シェーダーに、'common/trigger'を使います。これによってゲーム中では、このブラシは透明になります。このトリガーブラシは、プレイヤーに爆破可能なオブジェクトが近くにありますよと知らせるのと、このブラシ中にダイナマイトを設置しなさい、という範囲を定めます。


6. トリガーブラシが選択されている状態で、2Dウィンドウ上で右クリックを押し、'trigger/trigger_objective_info'を選んでください。


7. トリガーブラシが選択されている状態で、Nを押してください。Entityウィンドウが表示されます。キー入力欄に、'track'と入力し、値入力欄に'the Dynamite Tes'と入力し、Enterを押してください。今入力したものは、プレイヤーがトリガブラシに触れると表示されるものですので、自分の望むものを設定しましょう。実際には「You are near 〜」と表示されます。
さて、まだまだ入力しなければならないキーと値があります。まず、キーに'shortname'、値に'Dynamite Tes'を値に入力してEnterを押してください。これは、コマンドマップ上でプレイヤーのオブジェクトの隣に表示されるものです(ちょっとよくわかんないです、ごめんなさい by ika)。次に、キーに'objflag'、値に'4'を設定してください。これは、コマンドマップ上でそのオブジェクトの場所にダイナマイトのアイコンを表示させ、このオブジェクトがダイナマイトでのみ破壊できるとするためのものです。最後に、どちらのチームがこのオブジェクトを破壊するのかということを設定します。Axis_objectiveを左クリックすると、このオブジェクトはAxisの物ということになり、Alliedがこれを爆破することになります。Allied_objectiveを選ぶと、逆になります。
ikanatto補足:objflagは1〜4とあり、4は既に説明したようにダイナマイトでのみ破壊可能であることを表し、1は全ての武器でダメージを与えられ(窓などに設定)、2は爆発物なら全てダメージを与えられ(戦車など)、3はダイナマイトかCoのリモートで破壊可能ということを表しています。1と2には別にオブジェクトの破壊されるまでに与えるダメージを設定しなければなりませんが、詳しくは他のチュートリアルを見てください。たぶんそのうち和訳します。たぶん。


8. 次に、トリガーブラシを選択し、その後破壊されるオブジェクトを選択します。この順どおりに選択したら、Ctrl+Kを押してこの二つをリンクします。これによって、二つに'targetname'とランダムなものがキーと値として設定されます。リンクされている間なら、いつでもこの二つに同じキーと値が追加できます。

9. いよいよ実際にダイナマイト爆破の処理をするスクリプトを作ります。が、その前に、これから作るスクリプトが使えるようにするためのEntityを作らねばなりません。Escを押して選択状態を解除し、2D画面で右クリックを押して、script/script_multiplayerを選びます。これが選択されている状態でNを押し、キーに'scriptname'、値に'game_manager'と入力してEnterを押してください。再びEscを押して非選択状態にします。これでスクリプトが使えるようになります。(もし、既にこのEntityを作っているなら改めて作る必要はありません。)


10. Radiantを使っての設定はこれで終わりです。ではスクリプトファイルを作りましょう。(もし既にスクリプトファイルを作っているのなら、そのファイルに下のものを追加すればOKです)。新規のプレインテキストを作り、下のものを入力してあなたのマップが保存してあるディレクトリに、'あなたの作っているマップの名前.script'と保存してください。スクリプト中にあるwm_announce以下のダブルクオーテーションで囲まれた部分は、オブジェクトがダイナマイトで破壊された時にプレイヤーに表示されるメッセージですので、お好きなように変えてください。

game_manager
{
   spawn
   {
   }
}

dynamitetest
{
   spawn
   {
      wait 200
      constructible_class 3
   }
   death
   {
      wm_announce "Allies have blown up the Dynamite Test!"
   }
}


ご苦労様です。これで全ての設定が完了しました。ゲーム中では、破壊目標に近づけばダイナマイトのアイコンが表示されるはずです。つまり、これがダイナマイトで破壊できるということを表しています!

投稿者 ikanatto : 06:46 PM

MG42の作り方

翻訳元:http://www.splashdamage.com/forums/viewtopic.php?t=2658

製作サイドの人の発言を限定的に和訳。

ika注:
entityを作る際にくどく原点ブラシを含むようにといっているのは、ETには奇妙なバグと
いうか仕様のようなものがあり、原点が設定されていないと一連のスクリプトの処理がう
まくいかないそうです。ですので、騙されたと思って設定しておきましょう。
また、ここで紹介されているキーと値についてですが、これらの数値は自分が設置しよう
としているオブジェクトによって変わりうるので注意してください。

以下本文:

A. Radiantですること。

1. misc_gamemodelのEntityを作る(インポートしろということですな)。これが作業箱(工
事のときにペンチでつねる対象)になる。作ったら以下のキーを入力。

classname, misc_gamemodel
skin, models/mapobjects/cmarker/allied_crates.skin
model, models/mapobjects/cmarker/cmarker_crates.md3
targetname, construction_materials

2. 次に、箱のそばに立つどちらのチームが建設できるのかを表す旗となるEntityを作る。
それに、以下のキーを入力。

classname, misc_gamemodel
skin, models/mapobjects/cmarker/allied_cflag.skin
model, models/mapobjects/cmarker/cmarker_flag.md3
targetname, construction_materials
angle, 180 (Facing direction)
modescale, 0.4
spawnflags, 2 (Animated)
frames, 190 (Total frames)

3. 建設箱と旗などのセットを囲む、クリッピング(そのentityへの移動を制限)ブラシを
作る。作ったら、それらをscript_mover entityとする。その際に、原点ブラシを含むこ
とを忘れないように。全てのscript_mover entityは、それが使われるかどうかに関係な
くscriptnameのキーが必要である。

classname, script_mover
targetname, construction_materials
scriptname, construction_materials
spawnflags, 2 (This makes the script_mover solid)

4. 建設箱のまわりにTrigger_objective_infoのブラシを作る。これが実際にエンジニア
が作業を行なえる範囲となる。原点ブラシをを含むことを忘れずに。

classname, trigger_objective_info
targetname, construction_toi
scriptname, construction_toi
target, construction_target (Points to the func_constructible)
shortname, Construction MG (Command map name)
track, the Construction MG
spawnflags, 1 (This is constructible by Allied)

MG42 Entityを望む場所に作る。

classname, misc_mg42
targetname, construction_mg42 (Needs to be different because of the repairmg42 c
ommand)
track, construction_track (This links all the extra stuff together)
accuracy, 0.5
angle, 90

6. MG42を囲むように、2つの土嚢モデルを作る。

classname, misc_gamemodel
targetname, construction_extra
track, construction_track (This links all the extra stuff together)
model, models/mapobjects/miltary_trim/sandbag1_45s.md3
angle, 215

classname, misc_gamemodel
targetname, construction_extra
track, construction_track (This links all the extra stuff together)
model, models/mapobjects/miltary_trim/sandbag1_45s.md3
angle, 135

7. 最後に、func_constructible Entityを作る。これは大抵土嚢やMG42の前あたりに作り、
プレイヤーをクリッピングするのに使う。原点ブラシも作るのを忘れずに。

classname, func_constructible
targetname, construction_target
track, construction_track (This links all the extra stuff together)
scriptname, construction_script (The main script routine)
spawnflags, 8 (Allied construction)

概要:

func_constructble、土嚢、MG42は同じtrack keyを持つため、建設スクリプトの処理の際
にこれらが一つのものとして認識されて、建設完了の際には一度に出現し、破壊の際には
一度に消える。

MG42 Entityは違うtargetnameが必要になる。というのも、MG42を修理するコマンドには、
そのEntityを直接指定しなければならないからだ。このコマンドがないと、MG42は壊れな
くなる。

建設箱、旗、script_moverクリッピングブラシは、すべて同じtargetnameであるconstruc
tion_materialsとなっている。これにより、スクリプトは対象がconstruction_materials
とされた時には、これら全てを参照することになる。

最後に、Trigger_objective_info Entityはfunc_constructibleにリンクされる。

スクリプト:

Code:
construction_script
{
spawn
{
wait 200
constructible_class 2
trigger self startup
}

buildstart final
{
}

built final
{
setstate construction_extra default
setstate construction_mg42 default
setstate construction_materials invisible

// Some kind of UI pop-up to alert players
wm_announce "Allied team has built the construction!"
}

decayed final
{
trigger self startup
}

death
{
trigger self startup
// Some kind of UI pop-up to alert players
wm_announce "Axis team has destroyed the construction!"
}

trigger startup
{
setstate construction_extra invisible
setstate construction_mg42 invisible
setstate construction_materials default
repairmg42 construction_mg42
}
}


The code is an alternative way again of creating single staged constructions but
instead of duplicating the same commands over and over again in each of the dif
ferent functions. I use a single trigger function at the bottom of the routine w
hich does the same stuff.

(!訳不明瞭:このコードは、異なるファンクションごとに何度も何度も同じコマンドを
せずに一段階建造物の建造/破壊を行なうものです。私(製作者)は同じようなシングルト
リガファンクションをルーチンの下に置いています。)

投稿者 ikanatto : 06:45 PM

キャビネットに関する質疑応答の和訳

翻訳元:
http://www.splashdamage.com/forums/viewtopic.php?t=2751
http://dleblanc.dyndns.org/etdocs/cabinets.html

簡易回復キャビネット
(注:これは本当に簡易版です。見た目は普通ですが、プレイヤーはキャビネットを通りぬけられますし、キャビネットの見た目も変化しません。詳しくは後者のリンクをたどっていってください。)

問い:キャビネット置いたんだけど回復してくれないんだ。どうすりゃいいんだ?

[BXtreme]:

まず、misc_cabinet_healthのentityを作るのさ。(モデルをインポートしろってことです
な)

そうしたら、それに以下を加えてくれ:(Entityの設定で)
key: scriptname value: healthcabinet
key: targetname value: healthcabinet

次にこのキャビネットのまわりに、プレイヤーがこの範囲に入ったら回復させるというト
リガーブラシを作ってくれ。作ったらそいつの名前はtrigger_healにでもしておいてくれ
や。出来たらこれに次の値を設定してくれ。
key: healtotal value: 400
key: healrate value: 3
key: target value: healthcabinet
key: scriptname value: hcab_trigger_heal
key: targetname value: hcab_trigger_heal

これでどうよ?

healtotal valueはこのキャビネットが保持しているヘルスパックの合計回復量、healrat
e valueはプレイヤーが毎秒何ヘルス回復するかってことを決めてるから、自分の好きな
ように設定してくれよな。

アモキャビネットも手順はこれとまったく同じさ。設定する時にヘルスキャビネットと変
わるのは:
misc_cabinet_health = misc_cabinet_supply
trigger_heal = trigger_ammo
healtotal = ammototal
healrate = ammorate
こんなところさ。

問い:おお、確かに回復してくれたぜ。ダンケBXtreme!だけど、消費量に応じてキャビネ
ットの見た目が変わらないんだけどどうしたらいいよ?

他の人:ああ、そりゃそうだ。なんせこれはただ単にキャビネットのまわりに自動的に回
復するエリアを作っただけだからな。
もし、見た目も変化させたいとなるとちょっと面倒だぜ。詳しくはhttp://dleblanc.dynd
ns.org/etdocs/cabinets.htmlを見てくれよな。

// [ikanatto] そっちはまだ和訳してません。

投稿者 ikanatto : 06:43 PM

W:ETソースコード朗読会 補足1

Reading the Source Code of W:ET Appendix #1

飛行機はどこへ行く

ETは三次元空間で多人数が入り乱れるシューティングゲームです。これを実現するコードには当然ですが3次元空間の処理、通信、描画、その他様々なものが必要になります。

三次元空間には高さ、幅、そして奥行きがありますよね。この三つを指定することで、空間上の位置を決めることが出来ます。数学的には、三次元直交座標系で1点の位置を一意に決める必要十分条件が満たされたといえます。

三次元空間に飛行機が飛んでいたとします。先述したとおり、飛行機がどこを飛んでいるという「位置」は3つの情報から決定できます。しかしながら、どちらに向かって、まではわかりません。なぜなら、飛行機は原則的に頭の向く方向へ進む乗り物なので、「向き」に関する情報がなと動く方向を判断できないからです。

「向き」に関する情報まで含めた物理は高校では扱わないかと思います。しかし、世の中のあちこちにある「もの」は、ある大きさを持っており、高校物理や数学で扱う「点」とは違います。そして「もの」には形があり、向きによって色々物性が変化するものです。

だからといってみなさんに物理を勉強してきてくださいというわけではありません。「向き」も大事な空間情報であることを認識してもらえれば結構です。兎角、プレイヤーの向きが武器の狙いそのものに相当するFPSゲームでは尚更、ということです。

向きを表現する

ここから本題に入ります。

三次元空間上で、ある形と大きさを持ったものの向きを一意に指定したい時、いくつの情報が必要になるでしょうか。とりあえず、自分自身で確かめてみるのが一番確実でしょう。

上下にに首を動かしてみてください。あなたのあたまの位置はほとんど動かず視野(見える範囲)が左右に大きく変わりましたよね。これが「向き」の大事さを体現しています。そして首を左右に振っても同じく見える範囲が変わるかと思います。

続いてあたまを左右に傾けてください。見える範囲は変わりませんが、水平位置の時よりも景色が傾いて見えますよね。当然ですよね。

さあ、これで物体の方向を決める3要素をすべて見てきました。

上下、左右、そして回転。これが三次元空間上の物体の方向を決める全要素です。より一般的には、姿勢角と呼ばれています。そして上下左右回転の角度は、それぞれピッチ、ヨー、ロールと呼ばれます。ロボットや飛行機が好きな人は知っていたかもしれませんね。

数学的な表現は次のようになります。最初に、まっすぐを向いている状態の視線方向をx軸、垂直右方向をy軸、上方向をz軸とした直交座標系を設定します。すると、この座標系をz軸中心に回転する操作が首を左右に振ることに相当します。このときの角度がヨーです。同様にy軸中心ではピッチ、x軸中心ならロールです。

実例

私よりも詳しく的確に解説してくれているところがあるのでそちらを紹介します。

http://www.geocities.jp/gthu1df564/FreeR.html 最初の部分に姿勢角(オイラー角)に関する説明があります。

マイクロソフトMSDNライブラリ さすがOS作っているところですね。解説から実例まで見事です。サンプルコードもあります。

空間ベクトルと複素数に関するどっかの大学教授の文章 もっと詳しく知りたい人はこちらへ。こちらは私の脳みそレベルを超えているためサポート対象外となります。

投稿者 ikanatto : 06:36 PM | コメント (1076)

W:ETソースコード朗読会 第五回

Reading the Source Code of W:ET #5

今回の目標

武器個別の攻撃判定を前回前々回と見ました。今回はそんな武器によって傷ついた体を癒してくれるヘルスパックについてみていきましょう。ちなみにソースコード的にはこのヘルスパックも扱い分類としては「武器」です。つまり、マシンガン構えているのもヘルスパック抱えているのも敵対行為とみなされます。みなさん、戦場で救護班になっても敵はあなたを狙ってきます。注意しましょう。

ヘルスパックを出す

g_weapon.cの184行以降を見てください。メディックがヘルスパックを出す処理についてのコードが書かれています。早速コードを見ましょう。

void Weapon_Medic( gentity_t *ent ) {
	gitem_t *item;
	gentity_t *ent2;
	vec3_t	velocity, offset;
	vec3_t	angles,mins,maxs;
	vec3_t	tosspos, viewpos;
	trace_t	tr;

ここはいつも通りの宣言と初期化部分ですね。見慣れない構造体に、gitem_tというものがあります。定義を見てみましょうか。

typedef struct gitem_s {
	char		*classname;	// spawning name
	char		*pickup_sound;
	char		*world_model[MAX_ITEM_MODELS];

	(省略)

	int			giClipIndex;	// which clip this weapon uses.  this allows the sniper rifle to use the same clip as the garand, etc.

	char		*precaches;		// string of all models and images this item will use
	char		*sounds;		// string of all sounds this item will use
} gitem_t;

どうやらアイテムに関するプロパティを含む構造体のようですね。具体的にはクラス名、表示モデルデータ、拾った時の音などでしょうか。

本文の続きを見ていきましょう。

	if (level.time - ent->client->ps.classWeaponTime > level.medicChargeTime[ent->client->sess.sessionTeam-1]) {
		ent->client->ps.classWeaponTime = level.time - level.medicChargeTime[ent->client->sess.sessionTeam-1];
	}
	
	if( ent->client->sess.skill[SK_FIRST_AID] >= 2 ) {
		ent->client->ps.classWeaponTime += level.medicChargeTime[ent->client->sess.sessionTeam-1]*0.15;
	} else {
		ent->client->ps.classWeaponTime += level.medicChargeTime[ent->client->sess.sessionTeam-1]*0.25;
	}

二つのif構文があります。最初の条件節は、クラスごとのチャージが満タン以上になっていた場合、チャージをちょうど満タンに設定しなおしています。次の条件節では、プレイヤーのメディックスキルが2以上の場合にチャージ消費量を15%、そうでなければ25%に設定しています。

	item = BG_FindItemForClassName("item_health");
	VectorCopy( ent->client->ps.viewangles, angles );

	if ( angles[PITCH] < -30 ) {
		angles[PITCH] = -30;
	} else if ( angles[PITCH] > 30 ) {
		angles[PITCH] = 30;
	}

最初の行で、itemにはitem_healthをクラスネームとするアイテムのデータが代入されました。次の行にてanglesにプレイヤーの姿勢角がコピーされています。

後のifブロックでは、プレイヤー視野角のピッチ、つまり上下方向の角度をみています。これが±30度以上の場合、30度もしくは-30度に設定しなおします。

	AngleVectors( angles, velocity, NULL, NULL );
	VectorScale( velocity, 64, offset);
	offset[2] += ent->client->ps.viewheight/2;
	VectorScale( velocity, 75, velocity );
	velocity[2] += 50 + crandom() * 25;

ここでは、実際にヘルスパックを「発射」する速度や向きを設定しています。anglesの方向に大きさ64のベクトルがoffsetに代入され、高さ方向にプレイヤーの視点の高さの半分が加算されます。そしてヘルスパックの発射速度はanglesの方向に75の速さと上方向に50〜75がプラスされたものになります。つまり、見ている方向にまっすぐ出るのではなく、少し浮き気味に発射されます。私たちが実際ゲーム中で見るのと同じ状態ですね。

	VectorCopy( muzzleEffect, tosspos );
	VectorMA( tosspos, 48, forward, tosspos );
	VectorCopy( ent->client->ps.origin, viewpos );

	VectorSet( mins, -(ITEM_RADIUS + 8), -(ITEM_RADIUS+8), 0 );
	VectorSet( maxs, (ITEM_RADIUS + 8), (ITEM_RADIUS+8), 2*(ITEM_RADIUS+8) );

ここではヘルスパックが打ち出される場所とヘルスパックの大きさを設定しています。

	trap_EngineerTrace( &tr, viewpos, mins, maxs, tosspos, ent->s.number, MASK_MISSILESHOT );
	if( tr.startsolid ) {
		VectorCopy( forward, viewpos );
		VectorNormalizeFast( viewpos );
		VectorMA( ent->r.currentOrigin, -24.f, viewpos, viewpos ); 

		trap_EngineerTrace(&tr, viewpos, mins, maxs, tosspos, ent->s.number, MASK_MISSILESHOT);

		VectorCopy( tr.endpos, tosspos );
	} else if( tr.fraction < 1 ) {	// oops, bad launch spot
		VectorCopy( tr.endpos, tosspos );
		SnapVectorTowards( tosspos, viewpos );
	}

こちらは射出する最終準備です。もし、ヘルスパック発生位置が壁の中の場合、プレイヤーの少し後ろに射出位置を変更しています。

    ent2 = LaunchItem( item, tosspos, velocity, ent->s.number );
    ent2->think = MagicSink;
    ent2->nextthink = level.time + 30000;
    ent2->parent = ent; // JPW NERVE so we can score properly later
}

最後に、実際にアイテムを射出する関数を呼び出し、ent2にアイテムを登録しています。射出されてから30000ミリ秒後、つまり30秒後にMagicSink関数が呼ばれてこのアイテムは沈んで消える設定をし、この関数は全ての処理を終えます。

ヘルスパックを拾ったら

さて今度はヘルスパックを拾った場合についてみていきましょう。g_items.cの645行以降をみてください。

int Pickup_Health (gentity_t *ent, gentity_t *other) {
	int			max;
	int			quantity = 0;

	if( other->client->ps.stats[STAT_PLAYER_CLASS] != PC_MEDIC ) {
		if( ent->parent && ent->parent->client && other->client->sess.sessionTeam
		 == ent->parent->client->sess.sessionTeam ) {
			if (!(ent->parent->client->PCSpecialPickedUpCount % 
			MEDIC_SPECIAL_PICKUP_MOD)) {
				AddScore(ent->parent, WOLF_HEALTH_UP);
				G_LogPrintf("Health_Pack: %d %d\n", ent->parent - g_entities, other - g_entities);
			}
			G_AddSkillPoints( ent->parent, SK_FIRST_AID, 1.f );
			G_DebugAddSkillPoints( ent->parent, SK_FIRST_AID, 1.f, "health pack picked up" ); 
			ent->parent->client->PCSpecialPickedUpCount++;
		}
	}	

最初の宣言と初期化のあとに多重ifがあります。最初の条件は、拾おうとしている人がメディック以外かどうか、次にパックを出したものが存在し、かつプレイヤーで、しかもパックを出した人と拾おうとしている人のチームが同じかどうかをみています。さらに次のifでパックを拾った回数が4の倍数かどうかをみており、そうならば出した人にスコアを与えその旨ログに出力します。そしてパックを出した人にメディックのXPを1与えて、アイテムを拾った回数を1増やします。

	max = other->client->ps.stats[STAT_MAX_HEALTH];
	if( other->client->sess.playerType == PC_MEDIC ) {
		max *= 1.12f;
	}
	other->health += ent->item->quantity;
	if (other->health > max ) {
		other->health = max;
	}
	other->client->ps.stats[STAT_HEALTH] = other->health;

	return -1;
}

ここの部分で拾った人の体力を回復させています。まずはmaxへ規定の最大体力を代入しています。メディックならこれを1.12倍します。もし回復した体力が最大値を超えた場合、これを最大値へ再設定します。

最後に

今回は武器ではなく、ヘルスパックというアイテムを出す処理と拾った時の処理について見ました。ここで取り上げた以外にも、落ちているパックを拾う処理、パックを出すのにチャージが足りているかを判断する処理、拾ったパックを消す処理など色々あります。是非そういった部分もチェックしてみることをおすすめします。

投稿者 ikanatto : 06:32 PM | コメント (18)

W:ETソースコード朗読会 第四回

前回のおさらいと今回のねらい

第三回では具体的に、ゲーム中での弾丸当たり判定についての処理を見ました。これはサーバーサイドのコードでしたね。そのため実際に弾を発射した時に銃口に煙を出したり、弾丸の軌跡、着弾点のエフェクトなど、ビジュアル的な処理はここで行われていません。

今回は再び武器の一つとして、ナイフの処理を見ていこうかと思います。ナイフは素敵な武器です。背後から忍び寄ることが出来れば、弾薬の消費無しに相手を倒せます。何より、相手も倒されるとなぜか楽しい武器です。ベクトル演算が結構多いので、数学の参考書があるといいかもしれませんね。

ナイフで刺す

早速コードを見ていきましょう。g_weapon.cの85行以降あたりがナイフに相当します。

#define KNIFE_DIST 48

さて、冒頭に定数定義がされています。字面から、ナイフの射程距離を表すものと予想できます。48インチ、つまり1メートルくらいが有効範囲ということになります。ちなみにこれを10000くらいに変更すると、かなり離れた相手にも見えている限り確実・即座にヒットする強烈な武器になります。もちろん、背後に当たれば即死です。

続いて関数を見ていきましょう。

void Weapon_Knife( gentity_t *ent ) {

	trace_t		tr;
	gentity_t	*traceEnt, *tent;
	int			damage, mod;
	vec3_t		pforward, eforward;
	vec3_t		end;
	mod = MOD_KNIFE;

他の関数と同じですね。変数宣言と初期設定をここでしています。

	AngleVectors (ent->client->ps.viewangles, forward, right, up);
	CalcMuzzlePoint ( ent, ent->s.weapon, forward, right, up, muzzleTrace );
	VectorMA (muzzleTrace, KNIFE_DIST, forward, end);
	trap_Trace (&tr, muzzleTrace, NULL, NULL, end, ent->s.number, MASK_SHOT);

最初のAngleVectorsから見ていきましょう。この関数の定義部分は、q_math.cにあります。q_math.cは他にも色々な演算関数がと定義が入っていますので、一通り見ておくと今後の理解がスムーズかと思います。

void AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) {
	float		angle;
	static float		sr, sp, sy, cr, cp, cy;

	angle = angles[YAW] * (M_PI*2 / 360);
	sy = sin(angle);
	cy = cos(angle);

(省略)

	if (forward)
	{
		forward[0] = cp*cy;
		forward[1] = cp*sy;
		forward[2] = -sp;
	}
(省略)
}

まさに計算だけをする関数です。結果的にプレイヤーが見ている方向に応じた座標系軸ベクトルを返します。以下の解説は読みたい人だけ呼んでください。引数として、角度と結果を格納するベクトルが与えられます。この関数は、与えられた角度系(ヨー・ピッチ・ロールの三成分)から、直交座標系(xyz)の直行基底ベクトルを計算し、それぞれの基底ベクトルをforward、right、upとして返します。ピッチ、ヨー、ロール全てが0の場合、fowardは(1,0,0)、rightは(0,1,0)、upは(0,0,1)となって、標準の直交座標系になります。詳しい角度と基底ベクトルの変換については参考書などをご覧ください。

話を本筋に戻します。結局この行:

	AngleVectors (ent->client->ps.viewangles, forward, right, up);

についてですが、角度系としてent->client->ps.viewanglesを渡しています。つまり、ナイフで攻撃しようとしたプレイヤーが見ている方向角によって、それぞれforward、right、upに回転した直交座標系の軸ベクトルを代入します。

続いてCalcMuzzlePointですが、これはプレイヤーの位置と向き(先ほど得た座標系)と武器の種類に応じ、攻撃判定が発生する開始点座標を計算します。この結果がmuzzleTraceに代入されます。

VectorMA関数が後に続きますが、MAはMutiply & Addの省略です。つまりあるベクトルを定数倍したものを他のベクトルに加える計算をします。これ以外にもベクトルの基本演算がq_math.cにて定義されていますので列挙しておきます。

#define DotProduct(x,y)			((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])
#define VectorSubtract(a,b,c)	((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2])
#define VectorAdd(a,b,c)		((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2])
#define VectorCopy(a,b)			((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2])
#define	VectorScale(v, s, o)	((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s))
#define	VectorMA(v, s, b, o)	((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s))

それぞれの意味は名前と定義を見るとわかると思います。DotProductは内積、VectorSubstractは引き算、VectorAddは足し算、VectorCopyは単に代入、VectorScaleは定数倍です。

この計算によって、end = muzzleTrace + KNIFE_DIST * forwardが代入されました。つまり、endは攻撃判定の終端座標になります。

そして四行目にて実際に当たり判定をしています。当たった場合、trにその座標が代入されます。

次に進みましょう。

	if ( tr.surfaceFlags & SURF_NOIMPACT )
		return;

	// no contact
	if(tr.fraction == 1.0f)
		return;

この部分は当たり判定の結果を見て、何にも当たっていなければここで終了する処理をします。

	if(tr.entityNum >= MAX_CLIENTS) {	// world brush or non-player entity (no blood)
		tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
	} else {							// other player
		tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
	}

ここはナイフの当たったものがプレイヤーなのかそれ以外のものかを判断しています。

	tent->s.otherEntityNum = tr.entityNum;
	tent->s.eventParm = DirToByte( tr.plane.normal );
	tent->s.weapon = ent->s.weapon;
	tent->s.clientNum = ent->r.ownerNum;

	if(tr.entityNum == ENTITYNUM_WORLD)	// don't worry about doing any damage
		return;

この部分はtentに対しての代入操作が中心です。2行目にDirToByteという変な関数がありますね。これは、通信データ量削減のためのもので、162のあらかじめ決定されている代表的なベクトルにもっとも近いものを探し、何番目が最適かを戻り値とします。このため、3つのfloatからなるベクトルを1つのcharにすることが出来ます(精密さは欠きますが)。

最後のifは、当たったものがWORLD、つまりマップオブジェクトであるから何にもダメージ与えられなかったよ、ということでここで処理を終わります。実際クライアント側ではプレイヤーでなくとも壁などに当たればシャキシャキ音がしますが、サーバー側ではこのような処理は一切行われていないことがここでわかりますね。

	traceEnt = &g_entities[ tr.entityNum ];

	if(!(traceEnt->takedamage))
		return;

ここでは当たったものがダメージを受けうるものかどうかを判定しています。そうでないならここで処理を終わります。

	damage = G_GetWeaponDamage(ent->s.weapon); // JPW		// default knife damage for frontal attacks

	if( ent->client->sess.playerType == PC_COVERTOPS )
		damage *= 2;	// Watch it - you could hurt someone with that thing!

無事ナイフがダメージを受けうるものに当たったと判定されると、いよいよダメージ量の計算になります。最初の行にG_GetWeaponDamageがあります。この関数は前々回にて取り上げて、武器の種類に応じてダメージ値を返す関数であることを確認しました。ですからdamageにはナイフの標準ダメージである10が代入されます。

次のifの条件が、攻撃者がCVOPSであるかを確認しています。もしそうなら、ダメージが二倍にする処理をしています。つまりCVOPSのナイフは20ダメージです。

	if(traceEnt->client) 
	{
		AngleVectors (ent->client->ps.viewangles,		pforward, NULL, NULL);
		AngleVectors (traceEnt->client->ps.viewangles,	eforward, NULL, NULL);

		if( DotProduct( eforward, pforward ) > 0.6f )		// from behind(-ish)
		{
			damage = 100;	// enough to drop a 'normal' (100 health) human with one jab
			mod = MOD_KNIFE;

			if ( ent->client->sess.skill[SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS] >= 4 )
				damage = traceEnt->health;

		}
	}

	G_Damage( traceEnt, ent, ent, vec3_origin, tr.endpos, (damage + rand()%5), 0, mod);
}

最後の部分を見ていきましょう。ここが一番面白いところです。まず始めのifで、当たった相手がプレイヤー(人)であるかを確認しています。もしそうなら、自分と相手の視線方向角をベクトル化し、内積を見ます。ベクトル内積の値は、ベクトルの向きが近いほど大きな正、また直角の時は0、反対向きでは負になります。今内積を取った2ベクトルは正規化ベクトルですから、大きさは1なのでこの内積の値域は-1〜1となります。つまり、内積が1に近いほど向きが揃っている、つまりは攻撃者が相手の背後から攻撃しているという意味になります。

内積が0.6以上(角度にして約±30度以内)の場合、以下の処理をしています。まずダメージが先ほどの10ないし20から、100に設定し直されます。ほぼ即死です。さらにプレイヤーのCVOPSスキルが4以上の時、ダメージが相手のヘルスそのものになります。そうです、どんな相手でも即死です。

最後にentityへダメージを与える関数を呼び出してナイフ攻撃処理は終わります。

まとめ

今回はナイフの攻撃処理を見ました。そしてベクトルの基本的な計算と扱い方、また内積の性質なども確認しました。みなさん中学・高校などで数学を学び、「一体こんなのどこで使うんだ」と疑問に思われている方もいらっしゃったかもしれません。しかし、数学とはこれ単体では意味のないものです。ドライバーやかなづちといったものと同じで、これを作用させて便利なものをつくる道具に過ぎません。

ということで、これからMOD作りを考えている方は、是非数学(高校生あたりの三角関数やベクトルを中心に)の復習をしてみてはいかがでしょうか?

投稿者 ikanatto : 06:28 PM | コメント (27232)

W:ETソースコード朗読会 第三回

前回のおさらいと今回のねらい

第二回では武器性能であるダメージと散弾性のデータ定義を見ました。ソースコード中にはこのように定数データが含まれていることを確認できました。今回は定数定義ではなく、実際にある行動が取られたときどのような処理をされるか、といったフローの一つを見ていきます。かなり本格的なコードですので、Cに慣れていない方はC言語のリファレンスを手元においておくと理解がスムーズだと思います。

鉄砲玉の気持ち

別にカチ込みヤクザの気持ちを推し量るわけではありません。銃口から発射された鉛弾が、どのように処理され壁なり人なりに命中するのか、について見ていきます。これを理解することにより、効率的なマシンガンライフをエンジョイするのが目的です。

この処理に関する記述は、同じくg_weapon.cにあります。3090行以降です。この中には武器一般の処理が含まれていますので、紹介したもの以外にも色々と発見できるでしょう。是非ブラウジングしてください。では早速見ていきましょう。

qboolean Bullet_Fire_Extended(gentity_t *source, gentity_t *attacker, vec3_t start, vec3_t end, float spread, int damage, qboolean distance_falloff) {
関数定義部分です。戻り値はtrue(1)かfalse(0)ですね。何が引数として要求されているのでしょうか。gentity_tとは・・・詳しい説明は省略しますが、「ゲーム中のあらゆるもの」を定義する構造体です。これを定義されると、「ゲーム中のもの」として様々なデータを持つことになります。sourceとはなんでしょうかね。弾を発生させたものと考えるのが自然でしょうか。attackerは攻撃者そのものですね。startはベクトルです。恐らく弾が発射された位置でしょう。endは弾が止まる位置、spreadは散弾性、damageがダメージ、distance_falloffは距離によってダメージが減少するかどうかについてのフラグでしょう。

さーっと読んでいくと、わけのわからない関数がいろいろと呼び出されています。それぞれの意味をさぐっていきましょう。

	G_HistoricalTrace(source, &tr, start, NULL, NULL, end, source->s.number, MASK_SHOT);

	// bullet debugging using Q3A's railtrail
	if(g_debugBullets.integer & 1) {
		tent = G_TempEntity( start, EV_RAILTRAIL );
		VectorCopy(tr.endpos, tent->s.origin2);
		tent->s.otherEntityNum2 = attacker->s.number;
	}

	RubbleFlagCheck (attacker, tr);

まず、G_HistoricalTraceについて。Historicalは辞書的な意味は「歴史的な」、後に続くTrace「追跡」で「歴史的追跡」?一体なんなのでしょうかね。こういう場合は定義部分を見るに限ります。ここでは詳細を省略しますが、プレイヤーとサーバー間でのデータやり取りの際に生じる時間的ずれ―つまりタイムラグを考慮した上で行う衝突判断関数です。はやい話が開始座標と終点座標を指定して、その間にぶつかった障害物の位置trに格納する関数です。アンチラグ機能はこのあたりに効いてきます。気になる方は、g_antilag.cの202行以降をチェックしてください。

次にRubbleFlagCheckですが、これは2935行以降に定義されています。ちょっと見てみましょう。

void RubbleFlagCheck (gentity_t *ent, trace_t tr)
{
	qboolean	is_valid = qfalse;
	int			type = 0;

	// (SA) moving client-side

	return;

一部変更してあります。ところで関数定義4行目にしていきなりreturnしています。ですから実質的にこの関数は何もしていませんが、次のコメントに、「cgameに移したよ」となっています。なぜでしょうか。それは後を見るとわかります。

	if (tr.surfaceFlags & SURF_RUBBLE || tr.surfaceFlags & SURF_GRAVEL)
	{
		is_valid = qtrue;
		type = 4;
	}
	else if (tr.surfaceFlags & SURF_METAL)
	{
//		type = 2;
	}
	else if (tr.surfaceFlags & SURF_WOOD)
	{
		is_valid = qtrue;
		type = 1;
	}
	
	if (is_valid && ent->client && ( ent->client->ps.persistant[PERS_HWEAPON_USE] ) )
	{
		if (rand()%100 > 75)
		{
			gentity_t	*sfx;
			vec3_t		start;
			vec3_t		dir;
			sfx = G_Spawn ();
			sfx->s.density = type; 
			VectorCopy (tr.endpos, start);
			VectorCopy (muzzleTrace, dir);
			VectorNegate (dir, dir);
			G_SetOrigin (sfx, start); 
			G_SetAngle (sfx, dir);
			G_AddEvent( sfx, EV_SHARD, DirToByte( dir ));
			sfx->think = G_FreeEntity;
			sfx->nextthink = level.time + 1000;
			sfx->s.frame = 3 + (rand()%3) ;
			trap_LinkEntity (sfx);

		}
	}
}

さーっと眺めますと、なにやらif文がたくさんあって、それぞれの条件がsurfaceFlagsと定数の比較になっています。つまり、弾の当たった部分の材質を調べて、それに応じて砂煙を上げたり、水しぶきを出したりする関数のようです。見た目を処理しないサーバーサイドにこのような処理は必要ないですよね。だから負担軽減化の為にcgameへ移されたのでしょう。

話を元に戻します。

	traceEnt = &g_entities[ tr.entityNum ];

	EmitterCheck(traceEnt, attacker, &tr);

	// snap the endpos to integers, but nudged towards the line
	SnapVectorTowards( tr.endpos, start );

EmitterCheckも同じく砂埃発生などの処理をしているものと思います。

さて注目て欲しいのが次のif構文です。

	if( distance_falloff ) {
		vec_t dist;
		vec3_t shotvec;

		//VectorSubtract( tr.endpos, start, shotvec );
		VectorSubtract( tr.endpos, muzzleTrace, shotvec );		
		dist = VectorLengthSquared( shotvec );

		if(  dist > Square(1500.f) ) {
			reducedDamage = qtrue;

			if( dist > Square(2500.f) ) {
				damage *= 0.5f;
			} else {
				float scale = 1.f - 0.5f * (Square(1000.f) / (dist - Square(1000.f)));

				damage *= scale;
			}
		}
	}

ifの条件がdistance_falloffですから、距離によるダメージ減少をここで処理しているようです。具体的にダメージが減少するのは、直線距離にして1500以上です。人間の高さが約80で定義されていますから、この数値の単位はインチと見るのが適当でしょう。とすると、1500はおよそ35メートルほどに相当します。2500以上離れると、ダメージは完全に半分になります。1500〜2500の間では、妙な計算が行われています。これに沿うと、1550よりも2450のダメージの方が大きいということになります。妙です。ETの謎です。

続いて見ていきましょう。

	// send bullet impact
	if ( traceEnt->takedamage && traceEnt->client /*&& !(traceEnt->flags & FL_DEFENSE_GUARD)*/ ) {
		tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH );
		tent->s.eventParm = traceEnt->s.number;

		if(AccuracyHit( traceEnt, attacker )) {
			hitClient = qtrue;
		}

	} else {
		trace_t tr2;
		// Ridah, bullet impact should reflect off surface
		vec3_t	reflect;
		float	dot;

		tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL );

		G_HistoricalTrace(source, &tr2, start, NULL, NULL, end, source->s.number, MASK_WATER | MASK_SHOT);

		if((tr.entityNum != tr2.entityNum && tr2.fraction != 1)) {
			vec3_t v;

			VectorSubtract( tr.endpos, start, v );

			tent->s.origin2[0] = (8192 * tr2.fraction) / VectorLength( v );
		} else {
			tent->s.origin2[0] = 0;
		}

		dot = DotProduct( forward, tr.plane.normal );
		VectorMA( forward, -2*dot, tr.plane.normal, reflect );
		VectorNormalize( reflect );

		tent->s.eventParm = DirToByte( reflect );
		tent->s.otherEntityNum2 = ENTITYNUM_NONE;
	}

デバック部分は省略しました。最初のif文は何をみているのでしょうか。traceEntとは弾のぶつかったものを指していて、これがダメージを受けるものかつクライアント(つまりプレイヤー)であるかを見ています。早い話が人に当たったかどうかです。当たっていた場合、AccuracyHitによって本当にクラアントに当たったのかどうか、攻撃者は存在するのかどうかについて正確にチェックし、これがOKならばクライアントヒットフラグが成立します(hitClient = qtrue)。

それ以外の場合、つまりクライアント以外の壁や水面などに当たった場合について見ると、再び衝突位置を探しています。ただし最初と違うのは、水面に当たってもそこを衝突点としない条件下です。そして前回と今の衝突点を比較して、同じようなら貫通もしくは反射という扱いになっています。反射後の飛距離などは摩擦力と飛行距離などに依存しているようです。

では最後の部分を見て見ましょう。

if ( traceEnt->takedamage ) {
		G_Damage( traceEnt, attacker, attacker, forward, tr.endpos, damage, ( distance_falloff ? DAMAGE_DISTANCEFALLOFF : 0 ), GetAmmoTableData(attacker->s.weapon)->mod );

		// allow bullets to "pass through" func_explosives if they break by taking another simultanious shot
		if( traceEnt->s.eType == ET_EXPLOSIVE ) {
			if(traceEnt->health <= damage) {
				// start new bullet at position this hit the bmodel and continue to the end position (ignoring shot-through bmodel in next trace)
				// spread = 0 as this is an extension of an already spread shot
				return Bullet_Fire_Extended(traceEnt, attacker, tr.endpos, end, 0, damage, distance_falloff);
			}
		}
	}
	return hitClient;
}

最初のif条件がtraceEnt->takedamageですから、ダメージを受けるものに当たった場合の処理です。直後のG_Damage関数で実際にオブジェクトへダメージを与えています。

次のifがなかなか面白いです。当てたものが破壊されるオブジェクトで、かつそのヘルスがダメージを下回った、つまり破壊されたとき、再びBullet_Fire_Extendedがコールされます。つまり、窓などを破壊した弾丸は貫通して背後の敵を襲うというわけです。怖いですね。

最後の最後でクラアントにヒットしたかどうかを戻り値としてリターンし、この関数は終了です。

まとめ

疲れました。ええ、私がです。とても長いですよね、ソースコードって。しかもこのようなものが何万行もあると思うと、プログラマーというのは凄まじい職業なのだなぁと改めて感心します。

今回はコードを読むだけでしたが、みなさんはそろそろ「あの数値を変えたらすごいことになりそうだ」とか「あそこでこの関数入れたらどうなるんだ」とか、そろそろ思い始めているのではないでしょうか。そうです、その「思いつき」こそがMOD作りの始まりです。誰もが同じ始まりです。私の場合、ソースコードが始まりではなくモデリングからでしたが、「こうだったらもっと面白いのに」と思う気持ちは同じです。あとは、実際にやるかどうか、この一点に尽きます。日本人は海外の人に比べてかなり消極的部分が強いと認めざるを得ません。しかし、一度本気を出すと持ち前の勤勉さと仕事の細やかさにおいて他国民の追随を許さない程の能力を持っていると私は確信しています。ですから、皆さんに是非本腰入れてMOD作りをしてもらいたいので、導入として今この紹介を行っている次第です。どうかよろしくお願いします。

投稿者 ikanatto : 06:16 PM | コメント (53)

W:ETソースコード朗読会 第二回

Reading the Source Code of W:ET #2

前回のおさらいと今回のねらい

前回まででソースコードを「読む」準備が出来ました。何を実際に読むかは、気分とコードの可読性次第ですが、今回は武器データを中心に実際見ていこうというお話です。武器なんか興味ないっ、というお方は今回のお話はすっ飛ばしてください。

武器ダメージ

とりあえず、皆さんが興味ある部分といったらこういうところなんでしょうか。早速見ていきましょう。でもどこを見ればよいのか―。最初はまずこの問題を解決していかなければいけません。

ソースコードを見る上で一番最初の、かつ難解な問題が、「どこにどんな機能を持ったコードが書かれているかを探し出す」ことです。ただぼーっとコードを眺めているだけでは始まりません。幸い、コードは役割によってファイルやフォルダごとに区切られています。最初はこのファイル名を手掛かりに開いて検索することが有効です。

では武器関係の情報はどこに書いてあるか調べましょう。基本的に私たちが扱うのは、gameフォルダのものです。なぜかは後々説明しますが、ゲームの基本的な処理はここで行われるため、と考えてください。このフォルダを開いて、武器っぽいファイルを探してください・・・。どうですか?g_weapon.cというファイルがありませんか?なんだかこれっぽいにおいがプンプンしますね。早速開いてみましょう。

とても長いテキストが開かれたと思います。では、軽く全体を眺めてください。読む必要はありません。わけのわからない文字が一杯並んでるなー程度で結構です。さて、それでは問題の武器の性能について言及している部分を探していきましょう。鋭い人はもう気がついているかと思いますが、2784行以降を見てください。G_GetWeaponDamageという関数が定義されています。関数名を見ると、 Get Weapon Damage、つまり武器のダメージに関する関数であり、中身を見れば武器ごとのダメージを戻り値とするものと予想がつきます。つまり、武器のダメージはここの数字によって決まっているのです。早速見ていきましょう。

Line 2884 in g_weapon.c:

int G_GetWeaponDamage( int weapon ) {
		switch (weapon) {
		default:
			return 1;
		case WP_KNIFE: 
			return 10;
		case WP_STEN: 
			return 14;
		case WP_CARBINE:
		case WP_GARAND:
		case WP_KAR98:
		case WP_K43:
			return 34;
		case WP_FG42: 
			return 15;
		case WP_LUGER:
		case WP_SILENCER:
		case WP_AKIMBO_LUGER:
		case WP_AKIMBO_SILENCEDLUGER:
		case WP_COLT:
		case WP_SILENCED_COLT:
		case WP_AKIMBO_COLT:
		case WP_AKIMBO_SILENCEDCOLT: 
		case WP_THOMPSON: 
		case WP_MP40: 
		case WP_MOBILE_MG42: 
		case WP_MOBILE_MG42_SET:
			return 18;
		case WP_FG42SCOPE: 
			return 30;
		case WP_GARAND_SCOPE: 
		case WP_K43_SCOPE: 
			return 50;
		case WP_SMOKE_MARKER: 
			return 140; // just enough to kill somebody standing on it
		case WP_MAPMORTAR: 
		case WP_GRENADE_LAUNCHER: 
		case WP_GRENADE_PINEAPPLE: 
		case WP_GPG40:
		case WP_M7: 
		case WP_LANDMINE: 
		case WP_SATCHEL:
			return 250;
		case WP_TRIPMINE: 
			return 300;
		case WP_PANZERFAUST: 
		case WP_MORTAR_SET: 
		case WP_DYNAMITE: 
			return 400;
	}
}

コード自体はswitch構文で構成された単純なものです。引数として渡された武器IDに応じてダメージを返すというものです。ちなみにここで使われている武器IDは便利です。どう便利なのかは後で紹介します。

さて、この関数を見るとダメージが一目瞭然です。ナイフは10ですか・・・、もうちょっとあってもいいのでは。他ステンが14、ライフル34、SMGとピストル18などや、空爆缶140、手榴弾250、パンツァー400などですね。ちなみに爆発物は「直撃」した場合のダメージです。「直撃」とは、インパクトの瞬間に距離が0の状態を指します。この中にトリップマインというものがありますが、一体なんでしょうね?気になりませんか。

弾の散らばり具合

ちなみにこの関数の次にこんな関数が定義されています。

Line 2837 in g_weapon.c:

float G_GetWeaponSpread( int weapon ) {
	switch (weapon) {
		case WP_LUGER:
		case WP_SILENCER:
		case WP_AKIMBO_LUGER:
		case WP_AKIMBO_SILENCEDLUGER:
			return 600;
		case WP_COLT:
		case WP_SILENCED_COLT:
		case WP_AKIMBO_COLT:
		case WP_AKIMBO_SILENCEDCOLT:
			return 600;
		case WP_MP40:
		case WP_THOMPSON:
			return 400;
		case WP_STEN:
			return 200;
		case WP_FG42SCOPE:
			return 200;
		case WP_FG42:
			return 500;
		case WP_GARAND:
		case WP_CARBINE:
		case WP_KAR98:
		case WP_K43:
			return 250;
		case WP_GARAND_SCOPE:
		case WP_K43_SCOPE:
			return 700;
		case WP_MOBILE_MG42:
		case WP_MOBILE_MG42_SET:
			return 2500;
	}

	G_Printf( "shouldn't ever get here (weapon %d)\n", weapon );
	// jpw
	return 0;	// shouldn't get here
}

構文は先程の関数とほとんど同じですが、こちらは何を定義してるのでしょうか。関数名を見ると、Get Weapon Spread(広がり)、弾の散らばり具合を定義しているものと思われます。具体的に見ると、ステンが200で最小ですから、小さい方が散らばりは少ないものと考えられます。そうすると、ステン<ライフル<SMG<FG42<ピストル<狙撃銃(狙撃モードでない)<立ちMGの順で散弾性が良いようです。意外なのはライフルの性能、かなり良いということです。それでも当たらないと嘆くあなたと私、照準を合わせる訓練を頑張りましょう。

まとめ

今回は、構文的にも内容的にも非常になじみやすい部分を紹介してみました。他にもダメージのように定数によって定義されているものはたくさんあります。移動速度、重力、火炎放射器の射程距離、地雷の数などなど。それらを見つけ出してにやりとするのも一つの楽しみかと思います。

投稿者 ikanatto : 06:13 PM | コメント (1486)

W:ETソースコード朗読会 第一回

Reading the Source Code of W:ET #1

今回のねらい

ガイダンスの導入部により一応ソースコードを「見る」準備が出来ました。早速ソースコードを見ていきたいところですが、少しお待ちを。今回はプログラミングそのものに経験がない人向けに、プログラムを読む上で必要になる知識を紹介し、スムーズな理解の助けとなればと思います。ですからプログラミング経験のある方は今回の話はなかったことにしてください。

ご存知かの通りETはQuake3エンジンベースです。このため物理演算やデータの構造体など、Quake3でも用いられるアルゴリズムが散見されます。ですからETソースコード理解の為に、まずはQuake3での物理演算などの各種処理、そして用いられるデータ構造などの基本的理解をすることがとても大切になってきます。

注意して欲しいことに、専門家クラスにまで精通する必要はない、ということです。もちろん精通することでMOD作成の大きな助けになります。しかし、今回のようにただコードを読むだけなら関数や構造体が何を意味するものか、と漠然とした理解で十分です。 ですから気楽に構えてくださいね。

C言語

ここはプログラミング言語に「まったく」なじみのない方向けのセクションです。経験のある方はすっ飛ばしてください。

前回申したとおり、ETのソースコードはC言語です。プログラム言語としてはごく一般的ですが、日常会話でCを話す人はいません。C言語はコンパイラーに対してのみ意味のある言語だからです。そのため、初めてCを見る人にはCのソースコードを見ても、今まで見たことも聴いたこともない言葉で話しかけられるのと同じで理解できません。

そこで日本語とプログラミング言語の間を繋いでくれる辞書があります。リファレンスといわれるものです。幸いWeb上にリファレンスを載せているサイトがたくさんあります。ものによっては、丁寧な解説や例が載っています。

今一度申します。私たちは、「精通」する必要はありません。文を読んでなんとなく意味がわかればいいのです。ですから、リファレンス丸暗記の必要はなく、わからなくなった都度に参照すればいいのです。

とはいったものの、いきなりまったくの初心者が辞書だけで英文を読むことはなかなかに困難です。というのも、文には文法があるからです。これはプログラム言語でもまったく同じことです。単語単語の意味と、これらの並びや規則によって意味が決まります。

実例を前述のリファレンスを参照するなどして見てみることをお勧めします。ここなど、例と解説が丁寧になされているところはたくさんあります。自分にあったところを見つけて、ブックマークしておくといいと思います。

次回以降の本編に進むまでに、変数宣言、ポインター、構造体、数値演算、関数と戻り値などの基礎をなんとなーく理解してくれれば十分です。

数学 あると いいな

数学―。みなさんはどんな思い出があるでしょうか。学生の皆さんにとっては現在進行形で学習しているでしょうか。

このソースコード中で使われている数学的な要素で一番困ったちゃんと思われるのが、ベクトルです。しかもあっちこっちで出てきます。というのも、ETは言わずもがな、3Dです。ポリポリポリゴン満載です。3Dはご存知の通り、座標を表すためにxyzの3つが必要になります。この3つを一つのものとして表現したものがベクトルです。

ベクトルも普通の数字と同じように、足し算引き算、掛け算(内積外積)があります。実際にどんな計算をするのか忘れちゃった人は、昔の教科書を引っ張り出してくるかWebを検索してみてくださいね。

ベクトルにあって普通の数字にない要素に、方向があるということです。2つのベクトル演算の結果は、往々にして2ベクトルの方向の揃い具合によって大きく変わります。ETでもこの性質を利用して、ナイフでのバックスタブ判定、MGの射界制限に使われています。

ベクトル以外に、論理演算についても知っていると何かと有利です。論理演算とは、簡単に言えば0か1か、嘘か真かということを判定する計算だと思ってください。

そしてビット計算(二進数計算)もちょっと使われていますので、見慣れない計算が出てきたらこれも検索キーワードにしてみるといいかもしれません。

頻出構造体

私はCのコード理解はデータ構造の理解から始まるものと思っています。そこで、コード中で使われており、これだけは知っていて欲しい構造体について紹介します。

gentity_t これでもかというくらい、ソースコード各所に出現します。この宣言はg_local.hでされています。実際これはどんなデータかというと、ゲーム中で扱う様々なオブジェクト(英語でentity)全般です。オブジェクトの種類ごと、例えば武器・プレイヤー・弾・マップ中の物体などなどにそれぞれ分けて定義していたのでは面倒ですし、なにより拡張性と汎用性に問題が出てしまいます。そこで「もの」として広く定義され、様々なプロパティを設定できるようにされている構造体がgentity_tです。ソースコード中でこの宣言を見たら、何か意味のあるものなんだな、程度に考えてください。

vec3_t 数学のセクションで申しましたように、ETは3次元空間で位置や運動を計算しています。これらの扱いのためにはベクトルが便利です。このベクトルを構造体として定義しているのがvec3_tです。中身は単にfloatが3つです。それぞれ、xyzに対応しています。

qboolean 論理演算のためのものです。つまり真か偽かをそれぞれqtrueもしくはqfalseとして格納します。はっちゃけ0か1かです。

これ以外にもよく出てくる構造体はたくさんありますが、全て紹介するわけにはいきません。そこで、不明な構造体に出会ったら、是非定義箇所を検索してみてください。テキストエディターでソースコードを見ている場合でも、grepなどの検索ソフトを使えばコードファイル群から探し出すことが出来ます。

まとめ

理解の助けとなる知識の紹介だけしました。具体的なことは何ひとつしません。次回から実際のコードを見ていきます。

ここで紹介したことが、ソースコードを読んでいてわけのわらない部分に出くわした時、どうすればよいかの指針となれば幸いです。

投稿者 ikanatto : 06:10 PM | コメント (1567)

W:ETソースコード朗読会 ガイダンス

Reading the Source Code of W:ET :Guidance

はじめに What would we discuss?

皆様こんにちは。ネタに窮する余りこんな二番、三番煎じ以上に使い古されたネタで恐縮です。ここはソースコードを読むことで動作原理を知り、プレイの助けや改造の一歩となるようなものを発見していこうというものです。こんなネタGoogleなんかで検索すると、それこそ星の数ほど出てきますが、何より私自身の興味本位のための企画なので、どうかご容赦を。一応私日本人のつもりですが日本語を誤る場合もありますので、本文中変な表現に出くわしても驚かず、落ち着いて避難経路を確認して速やかに死んだふりをしてねぎをおしりから挿入してください。

ソースコード入手 Getting the Source Code

私自身、職業プログラマーでも何でもない単なるゲーマーですから、ソースコードの正確な意味は?と聞かれたら返答に困ります。私がここで取り上げるものは、Splash Damage(以下SD)とかid softwareがリリースしたWolfenstein:Enemy Territory(以下ET)というゲームです。これの実行ファイルを構成するプログラム、いわゆるソースコードを中心に見ていきます。このソースコードはSDのサイトにて公開されていますので、実際のものを見てみたいのであれば、是非ダウンロードしてください。ちなみにここで公開されているのはゲーム部分のみであり、画面の描写や通信などのエンジン部分は未だに公開されていません。こちらはidのライセンス販売部分なので、時期が来れば公開されるでしょう。楽しみです。

ソースコードを扱う Browsing and Compling the Source Code

再三申しているように、私はプログラマーでもSEでも何でもありません。単なるゲーマーです。これから紹介するのはソースコードを読んだりコンパイルしたりする環境についてですが、これ以外でもご自身が使いやすいと感じるものを是非使ってください。そして使い勝手などについて是非教えてください。

プログラムコードは、摩訶不思議な文字や記号がひたすらに羅列したものですが、所詮はテキストです。これを読むだけならNotepadなどのテキストエディターがあれば十分ということになります。しかし、私どもはコードを呪文のように唱えるだけで不十分で、この意味を理解しなければなりません。そしてソースコードは何万行にも渡りますので、効率的にこれを操作する必要があります。

持っている方は結構なのですが、いきなり皆様にVisual Studioといった高価なアプリケーションを買ってくださいというわけにはいきません。ひとまずterapadや秀丸といったフリーもしくはシェアの多機能テキストエディターを手に入れることをお勧めします。そしてプログラムコードはこのテキストのままでは実行できませんから、これを実行ファイルにしてくれるコンパイラーが必要になります。ETのソースコードはC言語です。C++でもVisual CでもC#でもありませんが、これら上位言語は大抵Cを内包しますので、これらいずれかのコンパイラーを手に入れることをお勧めします。ちなみにMicrosoftはVisual Studioの骨格部分、.NET framework SDKを無償配布しています。この中にC++がありますから、コンパイルのみならばこれで可能らしいですが、私は方法がわかりかねます。お勧めはVisual studioを買ってしまうことです。

しかしETは折角無料のものですから、コンパイラーも無料で済ませたいあなたにBorland C++ Compilerをお勧めします。こちら無償提供されているにもかかわらず非常にアプリケーション・コンパイラーとして優れています。しかしながら、ETのソースコードをそのままコンパイルすると多少のエラーが出るそうです。こちらなどを参考にしてください。SDのフォーラムは情報満載なので、わからないことや困ったことがあったら是非検索してくださいね。答えがなくとも、同じように困っている人同士で解決策を紡ぎだすこともできますし。

ソースコードの構成 Structure of the Source Code

ここからはソースコードを扱う環境があることを前提にしていきますが、極力テキストエディターだけでも十分なように配慮していきます。

ソースコードをダウンロードして、展開したフォルダの中に2つのフォルダとlicense.rtfとReadme.txtが入っています。ライセンスの方は実際にコードを改変して何かしようと考える人は眺めてください。二次配布や著作権に関する重要項目があります。Readme.txtは全員見ましょう。サポートやVCなどについて言及されています。

さて、早速srcを開いてみましょう。いくつかのフォルダとファイルがあります。まずはこれらの説明をします。

botai(フォルダ):BOT AI。BOTの行動に関するコード群が収められています。BOTはシングル時での敵も含まれます。ETはマルチプレイ専用なのでこの部分は実質機能しません。よってコードとして完成していませんので、直接利用が不可能です。残念。

cgame(フォルダ):Client Game。クラアント側が処理する部分のコード群。クライアント側はサーバーから送られてきた情報を元にゲーム状況を構築し、ユーザーから入力された情報をサーバーに送ります。その処理などです。

extractfuncs(フォルダ):Extranct Functions。ソースコードに直接関係しません。コードを関数ごとに切り出すプログラムとバッチファイルが入っています。

game(フォルダ):Game。ゲームのコアとなる部分のコード群が入っています。改造する時は主にここをいじくることになります。

ui(フォルダ):User Interface。ユーザーとプログラムの架け橋部分、入出力やインターフェースに関わるコード群です。

wolf.dsw(ファイル):Visual C++やVisual Studioを持っているなら、これを読み込むことで即座にこれらソースコードを統括した環境になります。VC++を推奨する理由はこのためです。

今回のまとめ Summary

まとめるほど、何かやっていません。今回は準備です。次回は知っていると何かと便利な知識の紹介をします。

投稿者 ikanatto : 06:01 PM | コメント (725)

リンク

Link


あまりアンテナ巡らせてないのでリンクも少ないです。リンク貼ってもここから飛ぶ人もほとんどいないでしょうから、無断リンクご容赦ください><;;

ET関連会社


作ったもしくは販売している会社です。

id software
今更説明も要りませんよね。

Activision
世界最大ゲームディストリビューターです。凄まじい規模です。

Splash Damage
ETを中心的に作った会社です。

Graymatter Studio
RtCWのマルチプレイ部分を作った会社。すごい。でもサイトが若干サイコ入っている気がする・・・。

Even Balance
Punk Buster作っている会社です。


海外ET関連サイト


ETは日本はもちろんのこと海外でも遊ばれていますので、当然海外にも有用なサイトがたくさんあります。

Wolfenstein: Enemy Territory International Resource Site
海外ではETは欧米よりもむしろ東欧系の国々で非常に盛んに遊ばれています。そんな国の一つ、ドイツのこちら情報サイトは量もスピードも本家を凌ぐほど。クライアントもダウンロードできますし、マップなどもたくさんあるので探し物をする時はここが便利です。

Planet Wolfenstein
Game spy系列のサイトです。そこそこ盛り上がっています。


国内ET情報サイト


国内のETを扱うサイトです。リンクは一方的に貼っています。

Oops! RtCW
ここさえ見ればETするのに困らないくらい情報が満載です。しかも一つ一つの粒がしっかりしていますので、安心して読むことができます。ぜひ見ましょう。

卍 Enemy Territory 卍
初心者が必ずお参りする卍です。初めての人はぜひ見てください。


国内クラン


国内のETで活動をしているクランです。

S.W
我らがクランS.W。活動期間はETが世に出てからとほぼ同じ。しかし影薄い。MADさんがサーバー立ててくれています。内容は不定期に変わりますが。

N
S.Wと縁深くお付き合いのあるクランN。plaplaさんがHimatsubushiサーバーを立ててくれています。とても人気のあるサーバーです。

サーバー


電気代とネットワーク使用料、ハードウェア代金と設定時間などなど色々犠牲を払ってくれてまでサーバーを立ててくれている管理者にはただただ頭が下がるばかりです。アリガトウゴザマス。

TEIOU SEKKAI
ET初期から現在まで存在し続け、初心者から上級者まで広く受け入てくれる懐の広さには感服です。私はずーっとこのサーバーにお世話になりっぱなしで、こちらに足を向けて眠ることは出来ません。

Himatsubushi server
名前にあるように、楽しく暇をつぶすことを目的にしたサーバーです。が、なかなかスパルタンな設定になっており、特に重火器制限と狙撃がきついです。中級車以上を対象としたサーバーでしょうかね。

Raspberry
泣く子も黙る上級者御用達サーバー。誤って初心者が入るとまったく違うゲーム展開に困惑と衝撃を覚えること請け合い。なので私はほっとんど行きません(^^;;;;;

お役立ちサイト


その他かゆいところに手が届くサイトです。

Quake2のメッシュファイルmd2の構造について

RtCW - MDS構造

Q3エンジン系で使用可能なプログラマブルシェーダーリファレンス

RtCW系マップの作成チュートリアル

Q3A MOD作成チュートリアル(主にコーディング)

MD3構造

Milkshapeでモデリング及びMD3を作るチュートリアル

UT系プレイヤーモデル作成チュートリアル

SplashDamageのForum たいていの疑問はここで検索するとヒットします。

ET MOD List by nullSkillz. メジャーどころからマイナーなものまで網羅。

(A HREF="http://www.cycloserver.com/index.htm">cyclserver
UNIXでのサーバーの立て方(ETではないですが)とゲームに関する面白い話があります。

投稿者 ikanatto : 05:59 PM | コメント (0)

作った人

作った人はikanattoです。

連絡先:mak@s13.xrea.com
英語サイト:http://mak.s13.xrea.com/ET/
(Moved)

ETはマルチプレイヤー部分が無償提供されてからずーっと遊んでます。
22時くらいにTEIOU SEKKAIさんやHimatsubushiさんで遊ばせてもらっています。
特にTEIOU SEKKAIさんはひじょーによく遊ばせてもらってます。いつもすいません。
RtCWはやってません。

いちおうSWというクランなのかチームなのか寄り合いなのかに所属しています。
http://et_sw.at.infoseek.co.jp/
ですが非常に残念ながらクランとしての活動は最近なかなかありません。
そういうわけでもっぱらパブリックサーバーで一般プレイヤーとして遊んでいます。

好きなクラスはCoです。
好きな武器はナイフです。

FPSは他に各種MOD含めQ2とQ3Aをよくやっていました。
初FPSはDoom2です。電話回線で対戦していたとは今となっては考えにくいものです。
Q3AのUrban Terrorはお気に入りで、まだ遊んでいます。こちらではmakって名前で遊んでいます。

他にも話題になったゲームは大抵やってます。シリアスサムなんかいいですね。リアル系ですとRainbow sixかOFPがなかなかよいのではないでしょうか。
他にもRTS、RPG、SLGとジャンル問わず遊んでいます。Blizzard社のゲームはどれも非常にやりこんでいます。とても質が高くアフターケアもよい、欧米にしては非常に珍しいベンダーでありデベロッパーですよね。今現在はWC3を未だにちょっと遊んでいたりします。
最近のゲームを買うと、結構な確立でユーザー側で新たにゲームの要素を改変、もしくは追加できるツールが増えてきたように思います。こんなチャンスは最大限に生かしたいですよね。

投稿者 ikanatto : 05:53 PM

このサイトについて

 このサイトはWolfenstein:Enemy Territoryを中心に、普通のプレイヤーにとどまらず一歩踏み込んでゲームの仕組みを見たり調べたり作ったりしてみようー、と考えて作りました。基本的には私がやってみたこと、調べてみたことなどをコンテンツ化して置いておくだけですが、ここを見てくれた人から何らかのリクエストがあれば積極的に応えていきたいです。
 メインはETのスキンやサウンドなどの置き換え、もうちょっと手を伸ばしてコード変更などを紹介できればと思います。

 私個人は主にコードを見たり書いたり、オリジナルスキンを作ったりしてます。海外のMODチームでモデラーとスキナーしていますので、クレジットロールに私の名前が出る日が来るかも!?
 ゲーム自体の解説やプレイテクニックなどについては、oops!RtCWさんがとっても詳しくコアに説明してくれていますので、そちらをご参照くださいね。また解説以外にも最新ニュースやインサイダー情報も載せていらっしゃいますので、是非是非一度ご覧ください。
 Wolfenstein:Enemy Territoryにこれから挑む初心者の方は、こちらこちらにて操作方法をつかんでくださいね。
 今までは平易なhtmlを直接打ち込んでコンテンツを作っていましたが、このたびMovable Typeを導入しました。とても簡単にきれいな文書が作れるので感動しています。ただ、近頃の流行なのか小さなフォントと淡めの配色が気に入らないので、視認性を高めるために背景を暗く、文字は明るく大きくしています。こんなに見にくいデザインを好むとは、外人さんは目がよほどいいのでしょうかね?

投稿者 ikanatto : 05:44 PM