alchemistarkの日記

やったことのメモ

Entityについて解説したい 6

ではEntPropについてお話するよ

InputはEntityに命令するもの、KeyValueはEntityの設定
EntPropはメモリの読み書き*1

一番面倒だけど出来ることも多い

GetEntProp系は読み出し、SetEntProp系は書き込み
EntPropも型ごとに関数がある String、Float、Vector、Ent
EntityIndexを扱う時はDecimalじゃなくてEntを使う
関連関数多すぎるのでAPIページ貼るのは省略してSetEntPropのみで

SetEntProp · entity · SourceMod Scripting API Reference
最後2つの要素は省略可。一番最後のはプロパティが配列の場合のみ使う

そもそもEntityというかEntPropを初めて見かけるのって
恐らく無敵化するにはどうするか調べてるときじゃないかなって思う*2

SetEntProp(client, Prop_Data, "m_takedamage", 1);

最初これ見た時の感想、m_takedamageってなんやねんだったんだけど僕だけかな
ググっても出てこないし、何だこれはってなった SourceModのページにNetProps一覧とか置いてあるんだけどその中にもm_takedamageって無いんですけど!?

答えから言います m_takedamageはdatamapにあります。
とりあえずdatamapとはなんぞやは置いておいて*3
コンソールにfind sm_dumpと入力してください。

f:id:alchemistark:20180212000624p:plain

えっと、分かるものだけ説明します。

sm_dump_datamaps datamap.txt
とかするとdatamapの一覧が保存されます。
開くと大量のプロパティ名だかメモリ名だかが出てきます。

// Dump of all datamaps for "tf" as at 2018/02/11
//
//
// Flag Details:
//
// Global: This field is masked for global entity save/restore
// Save: This field is saved to disk
// Key: This field can be requested and written to by string name at load time
// Input: This field can be written to by string name at run time, and a function called
// Output: This field propogates it's value to all targets whenever it changes
// FunctionTable: This is a table entry for a member function pointer
// Ptr: This field is a pointer, not an embedded object
// Override: The field is an override for one in a base class (only used by prediction system for now)
//

CBaseEntity - _ballplayertoucher
- m_iClassname (Offset 92) (Save|Key)(4 Bytes) - classname
- m_iGlobalname (Offset 96) (Global|Save|Key)(4 Bytes) - globalname
- m_iParent (Offset 100) (Save|Key)(4 Bytes) - parentname
- m_iHammerID (Offset 104) (Save|Key)(4 Bytes) - hammerid
- m_flSpeed (Offset 108) (Save|Key)(4 Bytes) - speed
- m_nRenderFX (Offset 112) (Save|Key)(1 Bytes) - renderfx
- m_nRenderMode (Offset 113) (Save|Key)(1 Bytes) - rendermode
- m_flPrevAnimTime (Offset 136) (Save)(4 Bytes)
- m_flAnimTime (Offset 140) (Save)(4 Bytes)
- m_flSimulationTime (Offset 144) (Save)(4 Bytes)
- m_nLastThinkTick (Offset 152) (Save)(4 Bytes)
- m_nNextThinkTick (Offset 208) (Save|Key)(4 Bytes) - nextthink
- m_fEffects (Offset 212) (Save|Key)(4 Bytes) - effects
- m_clrRender (Offset 132) (Save|Key)(4 Bytes) - rendercolor
- m_nModelIndex (Offset 114) (Global|Save|Key)(2 Bytes) - modelindex

……

右側にclassnameだとかhammeridだか書いてあるものは、プロパティ名に関連付けられています。
なのでそこを変更すると対応するプロパティの値も変わります。

1行目のCBaseEntity - _ballplayertoucherってのはEntityのClass名
この種類のEntityはこのプロパティ(メモリ?)を持ってますよって感じ
例えばteam_round_timerで検索してみると

CTeamRoundTimer - team_round_timer

ってのが引っかかるので、これより下がteam_round_timerにEntPropする時に使えるもの一覧になります
型はm_の次の1文字2文字で型の頭文字付いてるのでそれで 絶対じゃないけど

んでdatamapに対してGetやSetをする場合、Prop_Dataを指定します。

SetEntProp(client, Prop_Data, "m_takedamage", 1);

再掲。


次。netprop。
ネットワーク化可能なうんたらって多分コレの事だとは思うんだけど…

ぶっちゃけコレも同じ。
一度ファイルに保存します。
中身はこんな感じ

CBaseDoor (type DT_BaseDoor)
 Table: baseclass (offset 0) (type DT_BaseEntity)
  Table: AnimTimeMustBeFirst (offset 0) (type DT_AnimTimeMustBeFirst)
   Member: m_flAnimTime (offset 140) (type integer) (bits 8) (Unsigned|ChangesOften)
  Member: m_flSimulationTime (offset 144) (type integer) (bits 8) (Unsigned|ChangesOften)
  Member: m_vecOrigin (offset 792) (type vector) (bits 0) (ChangesOften|CoordMP)
  Member: m_ubInterpolationFrame (offset 148) (type integer) (bits 2) (Unsigned)
  Member: m_nModelIndex (offset 114) (type integer) (bits 13) ()
  Table: m_Collision (offset 348) (type DT_CollisionProperty)
   Member: m_vecMinsPreScaled (offset 8) (type vector) (bits 0) (NoScale)
   Member: m_vecMaxsPreScaled (offset 20) (type vector) (bits 0) (NoScale)

使い方も似たようなもん。ただ、Prop_DataがProp_Sendになります。

ちょっと待ってCBaseDoor (type DT_BaseDoor)って何って思った人が出てくると思う
それを調べるにはclasslistを見る必要があるので
sm_dump_classesしてください。
中身はこちら。

// Dump of all classes for "tf" as at 2017/12/06
//

CBaseEntity - _ballplayertoucher
CFireSmoke - _firesmoke
CPlasma - _plasma
CBaseEntity - ai_ally_speech_manager
CBaseEntity - ai_battle_line
CBaseEntity - ai_changehintgroup

…中略…

CBaseEntity - func_croc
CBaseDoor - func_door
CBaseDoor - func_door_rotating
CFunc_Dust - func_dustcloud

CBaseDoorありました。func_doorとfunc_door_rotaingですね。
尚、さらに下の方に行くとfunc_waterもCBaseDoorなのが分かります。
このように似たようなEntityは同じプロパティを持っていたりします。
NetProp使う時は一旦classlistからEntity名検索して該当のクラスを探してください。

tempentity propsってなんですかぼくはしりません

とりあえずここまで説明したInput、KeyValue、EntPropでEntityの操作は大体できると思います。
InputやKeyValueで操作できる内容ならなるべくそちらの方がいいかもしれません。
InputやKeyValueにもよるのですが、1つのInputやKeyValueの操作で、複数のPropが変更されている事があるようです。
その場合、変更が必要なPropが全て分かっているのなら良いのですが、そうでなくPropの設定変更が不足していた場合想定外の動作をする可能性があります。
デバッグ繰り返して問題なければ大丈夫だと思いますが…

6回に渡って長文書きましたが、日本語怪しすぎて読めないとは思いますが
入門者の助けになれば幸いです。
良いプラグイン開発を

*1:だと僕は思ってる

*2:偏見

*3:自分も分かってない

Entityについて解説したい 5

いい加減雑になってきたEntity講座にようこそ

1度書いた下書きが消えた 悲しい


今回はKeyValueについてちょっと話します

Entityのページとかで最初の方にあるKeyValueの一覧
あれは何かって話なんですが、HammerでEntityのプロパティ開いたら出るやつです。

f:id:alchemistark:20180209172101p:plain
ついでにヘルプも開いておいた

設置したEntityの名称やらオプションやら設定するとこですね
SmartEditを外した時に出てくるのが正式なプロパティ名なのでSourcePawnからはそちらを使います。
ただ、この画面では全てのプロパティは表示されていないので、一部のプロパティをSmartEditを使わずにHammerから設定するにはKeyとValueをAddする必要があります。
まぁHammerからやる場合はSmartEditでやるだろうから関係ないかな…

SourcePawnからKeyValueを設定する関数はDispatchKeyValue、DispatchKeyValueFloat、DispatchKeyValueVector
それぞれString、Float、Vectorに対応。

DispatchKeyValue · sdktools_functions · SourceMod Scripting API Reference
DispatchKeyValueFloat · sdktools_functions · SourceMod Scripting API Reference
DispatchKeyValueVector · sdktools_functions · SourceMod Scripting API Reference

プロパティの型はEntityのページやヘルプを参照。実際にHammerで値を設定してみて何が入るか確認するのもいいと思う。

new ent = CreateEntityByName("prop_physics_override");
DispatchKeyValue(ent, "targetname", "prop_1");
DispatchSpawn(ent);

使い方と言うほどのものでもないけど、こんな感じでプロパティを設定できる。
作ったEntityに名前付けておけばFindEntityByClassnameで検索した時に特定が容易
CreateEntityByNameはそのまんまEntityを作成する DispatchSpawnしないとゲーム内に出現しないので注意
プロパティの設定はスポーン前でも後でもできるけど、スポーン前だと反映されないプロパティとかあるので
スポーン後にまとめてでもいいかもしれない。

CreateEntityByName · sdktools_functions · SourceMod Scripting API Reference
DispatchSpawn · sdktools_functions · SourceMod Scripting API Reference


次はEntPropの話するよ ようやく本題感ある

Entityについて解説したい 4

Outputについて話しましょうか

話すとは言ったけどぶっちゃけOutputについて話すことあまりない

Entityがゲームの進行や与えられたイベントをトリガーにして別のEntityのInputを叩くのがOutputなわけですが
Outputを設定できるのはHammerからMAPを作成する時だけです。*1
そのあたりの詳細はHammer講座を見てください。

では、SourcePawnからOutput関連でできる事はなにか
端的に言うと、
・EntityがOutputを発信したのを検知する
・Outputを発信させる
の2つです。

OutputのHookは、HookEntityOutputとHookSingleEntityOutputの2つ
使い方は大体EventのHookと同じ…でもないけどそんな感じ
指定したクラスのEntityが、指定した名称のOutputを発信したら検知する的な。

各関数については例によってAPIページ見てね
HookEntityOutput · sdktools_entoutput · SourceMod Scripting API Reference
HookSingleEntityOutput · sdktools_entoutput · SourceMod Scripting API Reference

HookEntityOutputだと指定の名称のEntity全てHookしてしまうので、EntityIndexや名称など対象を識別できる情報を控えておくこと
HookSingleEntityOutputだと指定したEntityだけをHookできるけど、うまくいかない事例があったのでケースによっては諦めることになるかも。

あとOutputというよりEntityに対するEventをHookしたいならSDKHooks
SDKHook · sdkhooks · SourceMod Scripting API Reference
これはちょっと違う話になるけど、プラグイン作る時に求めるのはこっちな気はする
ただしこっちは個別のEntityにしかHookできないので、そのあたりは面倒っちゃ面倒

OutputのFireについては前書いた記事を参照のこと
alchemistark.hatenablog.jp

開発版でしか使えないことだけに注意。

後、基本的にOutputはマップに仕込まれたものである以上
マップの中身を把握していないと使えないし
分かるのならば直接Inputを叩いた方がいい

1つのOutputで複数のInputを叩いているとか、絶対に起こり得るイベント(セットアップタイムの終了時とかラウンド終了時とか)
によって引き起こされるイベントを起こすとか、そういう時にしか使えなさそう

Outputを発生させる要因自体は発生しないので勘違いしないように
例えばOnSetupFinishedをFireしてもセットアップタイムはそのまま継続する

*1:SourcePawnから設定できるなら教えてほしい

メモ

接点式のロータリーエンコーダ使うならチャタリング除去が必要そう

www.marutsu.co.jp

回路完全に忘れたからアレだけどたしか基本的に負論理だった気がするからこのままでいいはず



フリップフロップの出力をまとめるにはORでいい
出力反転を使うならORじゃダメって話だったと思う(前の記事で負論理をORでまとめるなって話)
そもそも出力をまとめた後にNOTかければいいのではないだろうか、7414の回路余ってるし。
ただし配線は死ぬ

f:id:alchemistark:20180204231131p:plain

もう回路読めなくなってるのでこれでいいのかすら分からない
一旦ブレッドボードで作るべきだなこれ

Entityについて解説したい 3

InputとOutputについて解説すると言ったな

します


まずEntityのInputとOutputとは何か、なのですが
単純にEntityに何かを入力する、Entityが何かを出力するという認識でOKです。

僕はオブジェクト指向プログラミングというのがよく分かっていない人間なのでこういう說明をするのもなんなのですが
EntityClassnameはそのままクラス、Entityの実体はインスタンス、InputとOutputはメソッドと返り値みたいな感じです。
また別に解説するとは思いますが、インスタンスのようにポンポンEntityを作成する事も可能です。

ブン投げるようでアレですが、InputとOutputに関してはHammerを起動した方が早く分かります。
Hammer講座でドアを作っている部分の解説がそれに当たります。

Valve ハンマーエディタ講座 4.1

Hammerだと、設置したEntityがプレイヤーの動きやゲームの進行によってOutputを発信するように設定し、
そのOutputを別のEntityのInputに送り込むようになっています。*1

SourcePawnからInputを行う場合とは流れが少し違うのですが、まずEntityのInputとOutputがどう設定されているかを上記ページでおおまかに理解しておいてください。

では、SourcePawnからInputとOutputを操作するにはどうするか

とりあえずはInputから。
例としてやはり"team_control_point"を使いますよ
Entityのページを復習しておいてね
team_control_point - Valve Developer Community

new ent = -1;
while((ent = FindEntityByClassname(ent, "team_control_point")) != -1){	//名前からエンティティを探す
	AcceptEntityInput(ent, "Disable");
}

関数のリファレンスはこちら
AcceptEntityInput · sdktools_entinput · SourceMod Scripting API Reference

これでDisableというInputを叩いたので、CPが無効化されます。個別のチェックしてないから見つかったCP全部無効になるね

activator、caller、outputidは省略可能です。
ただし必要とするInputもあるので、上手く動かなかったら指定してやってください。outputidが必要なのは今のところ見たことがないです。
具体的には本来Outputを送るはずだったEntityや、起動したプレイヤーを入れてやると良いです。特になければ0でも。

使えるInputはEntityによって違うので、Entityのページは良く見るようにしましょう。
HammerからもInput名は見えますが、說明は無いので…

なお、KeyValueのところは違います。
HammerでEntityのプロパティを開いた時、最初に表示されているプロパティ一覧がKeyValueです。
Flagは隣のタブだよ

次は、引数を必要とするInputの場合。
例えばSetOwnerというInputは、チーム番号を受け取ってCPをそのチームに取得させます。

	SetVariantInt(TFTeam_Blue);
	AcceptEntityInput(ent, "SetOwner", client, client);

TFTeam_Blueは定数です 3が入ってます。 TFTeam_Redは2 要tf2.inc
今更だけどこの記事で説明してる関数はほぼ全部sdktools.incが必要です*2

SetVariantInt関数の説明はこちら。
SetVariantInt · sdktools_entinput · SourceMod Scripting API Reference

Sets an integer value in the global variant object.

グローバルバリアントオブジェクトに整数値を設定します。

なんだよグローバルバリアントオブジェクトって。

…またよく分かってなくて申し訳ないのですが
僕は専用のバッファかなんかあるんかなぁという認識です。

SetVariant~関数でバッファに値を入れて、AcceptEntityInputでInputを送信する時にバッファの値も一緒に送信しているんだと思います
確かAcceptEntityInputした時に中身消えたと思うから次同じ値使う場合も再度SevVariant~した方がいい…はず した方が無難

SetVariantうんたらという関数が幾つかあるのですが、
これらはInputの引数の型に対応していて
与える値の型に対応した関数を使う必要があります。

んで

	AcceptEntityInput(ent, "SetOwner", client, client);

ここでactivatorとcallerにclient番号入れてますが、SetOwnerはactivator渡さないと動きません…
ポイント取得者を表示するために必要な情報なのだと思います。*3

とりあえずこれでInputは送信できるはず
次はどうしようかな OutputにするかKeyValuleにするかSpawnにするか
EntPropは最後にしようと思ってる*4

*1:HammerだとInput側(受ける側)にもどのEntityからOutputが送られているか表示されるので便利 Input(受ける)側では設定できないのに注意

*2:これは酷い

*3:0でも動くかなどうかな

*4:これが一番有益じゃねーの何で最後やねん

Entityについて解説したい 2

InputとOutputについて解説すると言ったな?
あれは嘘だ

InputとOutputするにはまずInputOutputしたいEntityを見つける必要がありますね
InputOutputについてはそれから

ここでは例として"team_control_point"エンティティを探してみるよ
これはキャプチャーポイントのEntityで、A/Dや5CPやKOTHに設置してあるアレね。
なお、画面に表示されてるCPのHUDやキャプチャー可能なエリアを設定してるのは別のエンティティだよややこしいNE

team_control_point - Valve Developer Community
Entityの詳細ページはこちら。

Entityを探す関数はFindEntityByClassname関数

FindEntityByClassname · sdktools_functions · SourceMod Scripting API Reference
関数の説明はこちら。

この関数は、指定したEntity番号以降のEntityの名前を順番に検索していって、最初に指定の名称のEntityを見つけたらそのEntity番号を返します。
なので検索して出てきたEntityが、自分の求めているEntityかどうかはまた色々しないと分かりません。

使い方。はいこれテストに出まーす。

new ent = -1;
while((ent = FindEntityByClassname(ent, "team_control_point")) != -1){	//名前からエンティティを探す
	PrintToServer("Entity %d はteam_control_pointです",ent);
}

まず、初期値が-1なので、存在する一番若番のEntityから検索を開始します。
Entityのクラス名が"team_control_point"のEntityを発見したら、そのEntity番号を返してくるので、entの値が更新されます。
見つかったEntity番号に対して処理を行った後、そのEntity番号を引数に再度検索します。
Entityが見つからなかったら-1が返されるので、ループを抜けます。

つまり、Entity番号が0から9の10個のEntityがあったとして、そのうち2と5がteam_control_pointだったとすると、

Entity 2 はteam_control_pointです
Entity 5 はteam_control_pointです

となるわけ。
例えば、kothとかだとCPは1つですが、5CPだと5つあるので上記の例だとPrintToServerが5回実行されるはず。

何故ループを回す必要があるかは単純に同じ種類のEntityが複数あった場合、探しているEntityが検索した1件目とは限らないため。
Entity番号がどう振られているかは決まっていないので、上記の場合探しているteam_control_pointが2か5かは分からない。今回2だったからって次も2とは限らないし、4になってるかもしれない

では中央CPだけ取得したい場合はどうすれば良いか、なのですが
GetEntPropでm_iDefaultOwnerが0のCP探せばいいんじゃないかな多分
これの說明はまた後でします。
基本的には総当りして、条件に合致するEntity見つけたら抜ければOK

とにかくこれでteam_control_pointのEntity番号が分かりました。

次こそInputとOutputやるよ 多分

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です。

*1:ごく例外として、Solidを生成して消えてしまうEntityもあります…prop_staticとかがそうです

*2:Entityのプロパティに何があるか確認するだけでも凄い助かる