Entityについて解説したい 1
詰まるようなところでは無いのかもしれないが、自分は延々と詰まったので解説したい
だがどこからどう解説したものか…
「自分はこういう認識をしている」という体で話します。
実際どうであるかとはかけ離れている可能性は大いにあります。
Entityとは、ゲーム上に存在するほぼ全ての物がEntityです。地面を転がっている酒瓶から、プレイしているプレイヤーキャラクターもEntityです。
ここではそう認識します。ぶっちゃゲーム内で管理されていていて個別の番号を割り振られているオブジェクトの事をEntityだと思ってて特に困らないと思います。
逆に、Entityで無い物もあります。地面や壁等のsolidがそうです。ただし、Entity化されたSolidを除きます。*1
このあたりはマップ制作の話に被ります。Entityを分かるのに避けて通れないと思うので、プラグイン開発者はHammerの最低限の知識が必須です。*2
Valve ハンマーエディタ講座 4.1
必読ですよ というかHammerの資料少なすぎやしませんか…
こちら必要な事は一通り抑えてあり、記述している所を一通りやれば次に必要な事は自分で見つけられると思います。
尚、TF2のHammerEditerはtf2インストール配下のbinフォルダ内にあります。SourceSDKは不要です
大事なことなのでもう一度言います。SourceSDKは不要です
話が少しそれましたが、Entityは大きく分けて2種類あります。
・SolidをEntity化したbrushエンティティ
・マップ上に直接設置、もしくはゲームエンジンが動的に生成するpointエンティティ
ポイントエンティティは実体を持つ(ゲーム上に表示される)物と、実体を持たない(表示されず、機能のみを持つ)ものがあります。
ブラシエンティティは例えばキャプチャーエリアの範囲やリスポンルームの範囲等を作る事ができます。
ポイントエンティティは弾薬やヘルスキット等のアイテムや、ゲームのタイマー、物理演算オブジェクト等多岐に渡ります。
Entityの一覧を貼っておきます。
どのようなEntityがあってどんな機能を持っているのか、見てみても良いかもしれません。
SourceEngine全般
List of base entities - Valve Developer Community
TF2
List of Team Fortress 2 Entities - Valve Developer Community
次があったらInputとOutputの話をします。
とりあえず今回はゲーム上の殆どのものはEntityなんだって思ってくれたらOKです。
KothIntelプラグイン
KothでCPの代わりにインテルを使うプラグイン。
インテルを拾っている間タイマーが動作します。落とすと止まります。
[TF2]KothIntel プラグイン pic.twitter.com/LtNrPKVjn7
— ArcMage (@Alchemist_Ark) 2018年1月11日
リスポン前に要塞作った園児がインテル持って立てこもるんですよね分かってます
ソースは貼りません(面倒になった)
希望者はご連絡ください
kothのタイマーを操作する
クソみたいに遠回りした…
とりあえず順番に語ります。本題だけ見たい人は最後のコードを見てください。
kothのタイマーを操作したい
とりあえず止めたり動かしたり時間を変えたりしたい
ではkothのタイマーはどこにあるのか?
とりあえず思いつくのはtf_logic_koth、tf_gamerules、team_round_timer
tf_logic_kothはとりあえずkothのHUDを表示してる
後タイマーにセットする時間を持っている
tf_gamerulesにはkoth関係と思われるInputはSetRedKothClockActiveとSetBlueKothClockActiveしかない
これはkothのタイマーをスタートさせるだけ 一方が動き出すともう片方は止まる。
team_round_timerは複数のマップをHammerEditerで開いてみたところ、
そもそもkothマップには置いてない事が多いように見える。
つまり、bspファイル内にkothのタイマーを記録しているEntityは存在しない…?
と思っていたらtf_logic_kothのページにこのような記述を発見。
It creates two entities named zz_red_koth_timer and zz_blue_koth_timer which have inputs like pause,resume,etc
これは、zz_red_koth_timerとzz_blue_koth_timerという名前の2つのエンティティを作成します。これらのエンティティには、一時停止、再開などの入力があります。
動的に作ってんのかよ!!!
じゃープラグインからzz_*_koth_timerってEntity探したり作ったりしてみようか
まで考えたけど見つからないし作成できず。
うん、僕バカなのでzz_*_koth_timerってClassnameでEntity探したよね
個別のEntity名ですってば。プロパティ名で言うとtargetname
恐らくEntityClassnameはteam_round_timerだと思われ
HammerEditerでOutput先にzz_~を指定するとOutput先が存在しないって言われるけど動作します。
何かのイベントが起きるとタイマーが止まるkothマップとか作れます。
zz_~で調べてたら、その名前で引っ掛けてEntityを特定してるコードを発見。
けどもう少し調べたらtf_gamerulesのNetPropに普通に格納されてた
んでtf_gamerulesはsdktoolsをincludeしたらGameRules_GetPropEntで読めるから…
あれ、これコード凄い単純じゃね…?
以下コード。
public Action:Cmd_get(client, args){ //タイマーのEntityを確保する new bluetimerEnt = GameRules_GetPropEnt("m_hBlueKothTimer"); new redtimerEnt = GameRules_GetPropEnt("m_hRedKothTimer"); new float:fBluTime; new float:fRedTime; //青タイマー確認 //止まっていると1、動いていると0 if(GetEntProp(bluetimerEnt, Prop_Send, "m_bTimerPaused") == 0){ //タイマー動作中(タイマー開始時の時刻-現在時刻) fBluTime = GetEntPropFloat(bluetimerEnt, Prop_Send, "m_flTimerEndTime") - GetGameTime(); } else{ //タイマー停止中 fBluTime = GetEntPropFloat(bluetimerEnt, Prop_Send, "m_flTimeRemaining"); } //赤タイマー確認 if(GetEntProp(redtimerEnt, Prop_Send, "m_bTimerPaused") == 0){ fRedTime = GetEntPropFloat(redtimerEnt, Prop_Send, "m_flTimerEndTime") - GetGameTime(); } else{ fRedTime = GetEntPropFloat(redtimerEnt, Prop_Send, "m_flTimeRemaining"); } //表示する PrintToChat(client,"Blue:%f Red:%f",fBluTime,fRedTime); PrintToChat(client,"BlueTimer:%d RedTimer:%d",GetEntProp(bluetimerEnt, Prop_Send, "m_bTimerPaused"),GetEntProp(redtimerEnt, Prop_Send, "m_bTimerPaused")); /* SetEntProp(bluetimerEnt, Prop_Send, "m_bTimerPaused",1); "m_bTimerPaused"に1を書き込むとタイマーが停止するが、同時に"m_flTimeRemaining"の値でタイマーが上書きされる 停止させる前に手動で"m_flTimeRemaining"の値を更新すること また、CPは取得されたままになるのでCPの所有者を開放する等の処置を取ること */ }
多分team_round_timeのInputが使えるので、全部GetEntPropする必要は無いと思う。
コメントにも書いてるけど、タイマーをただ停止させるとタイマーが動く前の時間に戻されるので注意。
先にTimeRemainingの値を書き換えておけばOK
そういえば両方動かしたら両方同時に動き出してちょっと笑える
ていうか最終的にコピペしたのがmorestocksなのちょっと納得行かない
つまりmorestocksをincludeした方が断然早い。
sourcemod-snippets/tf2_morestocks.inc at master · powerlord/sourcemod-snippets · GitHub
SouceModプラグインの開発を始めるにあたって必要なもの
SourcePawnのWiki作ろうって瞬間だけ思って放置してるやつに書いてたやつ。
準備するもの
MetaMod、SourceModが動作する環境
srcdsのインストールにて解説します。(機会があれば書きます)
SourcePawnのコンパイラ
SourceModがインストールされていれば、
addons/sourcemod/scripting/compile.exe
にあります。
また、ブラウザから利用できるコンパイラとしてSourceModの配布ページの
www.sourcemod.net
こちらか、
こちらが利用できます。
下の方はincファイルのincludeができるのでこちらの方が少々多機能です。
テキストエディタ
文字コード UTF-8(BOMなし)で保存できるエディタ なら何でも良いです。
プラグイン自体は文字コードを問いませんが、KeyValueを使用する時に使うファイルは UTF-8(BOMなし)である必要があります。
お好みのエディタがあるならばそれを、特にこだわらないならNotepad++を推奨します。
「Notepad++」オープンソースで開発されているプログラマー向けのテキストエディター
forest.watch.impress.co.jp
SourcePawn用のハイライト機能を使うことができます。
こちらで配布されています。
forums.alliedmods.net
プラグインになりますが、NppExecというプラグインを導入することによって保存してそのままコンパイラにファイルを送ることができるので、
こちらも導入を推奨します。
NppExecの使い方を解説しているページ
NppExec (コマンドの実行) | Notepad++のプラグイン解説
設定例
NPP_SAVE (インストールフォルダ)\addons\sourcemod\scripting\spcomp.exe "$(FULL_CURRENT_PATH)" powershell move $(NAME_PART).smx (インストールフォルダ)\addons\sourcemod\plugins\ -force
別にpowershellで無くても良いですが、サーバーパスがsmbの向こう側だったりすると必要です。
以上が揃えばプラグインを書く準備が整います。
Propの出現と破壊可能にする方法
ゴリゴリ書いてたのでとりあえず全部貼る。
public Action:Cmd_prop(client, args){ new ent = CreateEntityByName("prop_dynamic"); new filter; new String:temp[255]; //フィルター関係/////////////////////////// while ((filter = FindEntityByClassname(filter, "filter_activator_tfteam")) != -1){ GetEntPropString(filter, Prop_Data, "m_iGlobalname", temp,255); if(StrEqual(temp,"prop_filter")){ //既にフィルターが生成されていた場合、削除する AcceptEntityInput(filter, "Kill"); } } filter = CreateEntityByName("filter_activator_tfteam"); //フィルターを作成する SetEntProp(filter, Prop_Data, "m_iTeamNum",TFTeam_Blue); //フィルター対象チームを設定 DispatchSpawn(filter); //スポーンさせる //フィルター名を設定する SetEntPropString(filter, Prop_Data, "m_iGlobalname", "prop_filter"); //Propにフィルターを設定する //フィルター名とフィルターEntityが両方必要? SetEntPropString(ent, Prop_Data, "m_iszDamageFilterName","prop_filter"); //PropにフィルターEntityを設定 SetEntPropEnt(ent, Prop_Data, "m_hDamageFilter",filter); //Prop関係////////////////////////////////////// //Propのダメージイベントをフックする HookSingleEntityOutput(ent,"OnTakeDamage",EntityHook,false); //Propにモデルを設定する SetEntPropString(ent, Prop_Data, "m_ModelName", "models/props_badlands/barrel_flatbed01.mdl"); //衝突判定を設定 SetEntProp(ent, Prop_Data, "m_nSolidType",6); //Propをスポーンさせる DispatchSpawn(ent); //Propのヘルスを設定する SetEntProp(ent, Prop_Data, "m_iHealth", 200); //Propがダメージを受けるようにする SetEntProp(ent, Prop_Data, "m_takedamage", 2, 1); //Mortal //適当な位置に出現させる new float:vec[3]; GetClientAbsOrigin(client, vec); TeleportEntity(ent, vec, NULL_VECTOR, NULL_VECTOR); } public EntityHook(const char[] output, int caller, int activator, float delay){ PrintToChatAll("%d",GetEntProp(caller, Prop_Data, "m_iHealth")); }
大体コメントの通り
寝て良いよ
SetEntProp(ent, Prop_Data, "m_nSolidType",6);の値が6なのは何故か分かってない
一応プロパティのCollisionsの値がUse VPhysicsの時6になる。
KeyValueのキー名はsolid。
破壊可能にするだけならヘルスを設定してMortal化するだけでいいはず
ただし破壊された時何も表示せずただ消えるだけなので、破壊時にパーティクルを表示する等の配慮が必要。
フィルターは指定チームのみダメージを与えられるように設定してるので、誰でも壊せるならフィルター自体不要
なるべくフィルターには変な名前付けてマップ既存のフィルターと重複しないように注意した方がいいと思うけど大丈夫じゃないかな
あと言うまでもないけどTeleportEntityの座標がクライアントの位置なのでそのまま呼ぶと自分がPropにハマるから注意。
prop_dynamic - Valve Developer Community
filter_activator_tfteam - Valve Developer Community
追記
prop_physics_overrideを呼び出した場合、何故かHookSingleEntityOutputが上手く動作しないので
HookEntityOutputを使用した方がいいかも。この場合Entityの番号を保存しておくかユニークな名称を付けておく必要があるのでかなりめんどくさい。
prop_physics_override - Valve Developer Community
ていうかね!違いが全然分かってないんだよね!!!
prop_physics→モデル側で対応が必要
prop_physics_multiplayer→わからない モデルが動作中はプレイヤーがすり抜けてしまう?(モデルが静止するとCollisionが復活するのでプレイヤーが埋まる)
prop_physics_override→physics非対応モデルをphysics化?以降Entityのクラス名はprop_physicsになる
prop_physics_respawnable→知らない子です