▶ 実 行
▶ 実行
クリア
3Dモデルのアニメーションと投石(ThreeJS+AmmoJS)
by てぃふと@うぇいく
!「https://n3s.nadesi.com/plain/plugin_weykthree.js」を取り込む !「https://n3s.nadesi.com/plain/plugin_weykammo.js」を取り込む AMOベースURL=「https://weyk.sakura.ne.jp/storage/ammojs」 「全て」を実行速度優先 # 敵の移動に慣性を適用するかどうか。慣性が働くと曲がるとき横滑りするようになる。 慣性有はオフ # 動作確認で周囲を見渡す用。見渡せる代わりに投擲が出来なくなる。 オービタルコントローラはオフ # オンにすると平地、オフにすると高低のある曲面になる 地形平地はオフ # 床のテクスチャ選択。「グリッド」「芝生」のいずれか。それ以外にするとテクスチャ無しの単色。 床表現は「グリッド」 # 霧の有無を選択する 霧発生はオン # 投石が当たって倒された時に自動的に再出現する 討伐時自動再出現はオン # 予定していた行動が終了した際、待機時間後に自動的に再出現する 行動終了時自動再出現はオン 芝生URLは「http://localhost:3000/webapp/demo/js/threejs@1.2.8/examples/textures/terrain/grasslight-big.jpg」 グリッドURLは「https://n3s.nadesi.com/image.php?f=54.png」 モデルベースURLは「https://weyk.sakura.ne.jp/storage/gltf/」 モデルリストは{ ExpressiveRobot:{ 名前:"ExpressiveRobot", URL:「RobotExpressive/RobotExpressive.glb」, 回転補正: NULL, 位置補正: {軸: [0,1,0], 値: -2}, 拡大: [1,1,1], 鋼体: [1,2,1], アニメマップ: { 待機: "Idle", 歩行: "Walking", 走行: "Running" }, モデル:{}, アニメ:{} }, Soldier:{ 名前:"Soldier", URL:「Soldier.glb」, 回転補正: {x:0, y:0, z:PI}, 位置補正: {軸: [0,0,1], 値: -1}, Y補正: 0, 拡大: [2.5,2,2.5], 鋼体: [1,2,1], アニメマップ: { 待機: "Idle", 歩行: "Walk", 走行: "Run" }, モデル:{}, アニメ:{} }, Xbot:{ 名前:"Xbot", URL:「Xbot.glb」, 回転補正: NULL, 位置補正: {軸: [0,1,0], 値: -1}, 拡大: [2.5,2,2.5], 鋼体: [1,2,1], アニメマップ: { 待機: "idle", 歩行: "walk", 走行: "run" }, モデル:{}, アニメ:{} } } ユニットシリーズリストは{ 歩兵:{ 名前:"歩兵", モデル名:"ExpressiveRobot", スキン名:"" }, 兵士:{ 名前:"兵士", モデル名:"Soldier", スキン名:"" }, ボット:{ 名前:"ボット", モデル名:"Xbot", スキン名:"" } } 床辺長は200 # 200m四方(200m×200m×1m) 床辺分割数は128 # 格子状に区切ったmesh/shapeで扱う 床厚は1 # 1m 床反発率は0.7 壁高は50 壁反発率は0.7 重力は9.8 # 9.8m/s^2 石反発率は0.3 石半径は0.3 # 石個数は10 石重量は2 腕力は20 床回転摩擦係数は20 弾回転摩擦係数は20 床摩擦係数は20 弾摩擦係数は20 敵摩擦係数は0.0 地形高値は0 地形低値は-5 地形位相は9 衝突マージンは0.01 # 1cm WEBGLキャンバスはNULL レンダラはNULL ココはNULL カメラはNULL コントローラはNULL レイキャスタはNULL ランタンはNULL 物理ワールドはNULL CONFはNULL DISPはNULL BPHASEはNULL SOLVERはNULL 前回タイムスタンプは0 地面素材はNULL 地形はNULL 地面はNULL 床鋼体はNULL # Ammo適用用 # # Ammoでのstaticではない鋼体の鋼体とTJSモデルのペアの情報を全て持つ # フレーム毎の鋼体からモデルへの位置・姿勢反映に利用 変動物リストは空配列 石待機リストは空配列 敵リストは空配列 鋼体情報は{} 鋼体インデックスは0 見上は0 見上速度は0.02 # 共通敵性能情報 # 敵重量は50 敵回頭速度は45/180*PI 敵歩行速さは2.5 敵走行速さは4 敵最大歩行速さは5 敵最大走行速さは10 # モデル読込合流用ワーク変数 # モデル総数は0 モデル読込数は0 # 事前準備合流用ワーク変数 # 事前準備完了数は0 事前常備項目数は0 # 行動は、行動の名称と、その行動を続ける時間(秒)の配列 の配列 # 行動パターンは{ 右標準: [ ["右旋回", 1.5], ["走行", 2], ["左旋回", 3.5], ["歩行", 1], ["走行", 3], ["右旋回", 3], ["走行", 4] ], 左標準: [ ["左旋回", 1.5], ["走行", 2], ["右旋回", 3.5], ["歩行", 1], ["走行", 3], ["左旋回", 3], ["走行", 4] ], 右挟角: [ ["右旋回", 0.3], ["走行", 1], ["左旋回", 0.6], ["歩行", 2], ["待機", 0.3], ["走行", 1.5], ["右旋回", 0.6], ["走行", 3], ["左旋回", 0.6], ["歩行", 1.5], ["右旋回", 0.6], ["走行", 1], ["待機", 0.5], ["右旋回", 0.6], ["走行", 1.2], ["歩行", 0.8] ], 左挟角: [ ["左旋回", 0.3], ["走行", 1], ["右旋回", 0.6], ["歩行", 2], ["待機", 0.3], ["走行", 1.5], ["左旋回", 0.6], ["走行", 3], ["右旋回", 0.6], ["歩行", 1.5], ["左旋回", 0.6], ["走行", 1], ["待機", 0.5], ["左旋回", 0.6], ["走行", 1.2], ["歩行", 0.8] ] } ワールドマトリクスワークはNULL 開始する ●ライブラリ準備とは TJSライブラリ読み込み後には プラグイン準備 ここまで ここまで ●プラグイン準備とは プラグインリストは["loaders/GLTFLoader.js", "utils/SkeletonUtils.js", "libs/ammo.wasm.js"] もし、オービタルコントローラならば、 プラグインリストに"controls/OrbitControls.js"を配列追加 ここまで プラグインリストをTJSプラグイン読み込み後には AMMOライブラリ準備 ここまで ここまで ●AMMOライブラリ準備とは AMOライブラリ読込後には 事前準備 ここまで ここまで ●事前準備とは WEBGLキャンバス要素準備 描画準備 事前準備完了数は0 事前準備項目数は2 モデルリストをモデル読み込む レイキャスタはTJSレイキャスタ作成 描画中キャンバスの「mousedown」に「CVマウスボタン押」をDOMイベント追加する 描画中キャンバスの「mouseup」に「CVマウスボタン離」をDOMイベント追加する 描画中キャンバスの「mousemove」に「CVマウス移動」をDOMイベント追加する 事前準備完了チェック ここまで ●WEBGLキャンバス要素準備とは WEBGLキャンバスは"#three_cv"のDOM要素取得 Pは描画中キャンバス["parentNode"] もし、WEBGLキャンバスならば、 PからWEBGLキャンバスをDOM子要素削除 ここまで WEBGLキャンバスは"CANVAS"のDOM要素作成 WEBGLキャンバス["id"]は"three_cv" WEBGLキャンバス["width"]は描画中キャンバス["width"] WEBGLキャンバス["height"]は描画中キャンバス["height"] WEBGLキャンバス["style"]["display"]は「none」 PにWEBGLキャンバスをDOM子要素追加 ここまで ●後始末とは 描画中キャンバスの「mousedown」から「CVマウスボタン押」をDOMイベント削除する 描画中キャンバスの「mouseup」から「CVマウスボタン離」をDOMイベント削除する 描画中キャンバスの「mousemove」から「CVマウス移動」をDOMイベント削除する ここまで ●CVマウスボタン押とは もし、オービタルコントローラならば、 戻る ここまで EVTはWINDOW["event"] 要素位置はEVT["target"]からDOMクライアント境界取得 XはEVT["clientX"]-要素位置["left"] YはEVT["clientY"]-要素位置["top"] [X,Y]を狙い撃ちする ここまで ●CVマウスボタン離とは もし、オービタルコントローラならば、 戻る ここまで EVTはWINDOW["event"] 要素位置はEVT["target"]からDOMクライアント境界取得 XはEVT["clientX"]-要素位置["left"] YはEVT["clientY"]-要素位置["top"] #[X,Y]を狙い撃ちする ここまで ●CVマウス移動とは もし、オービタルコントローラならば、 戻る ここまで EVTはWINDOW["event"] 要素位置はEVT["target"]からDOMクライアント境界取得 XはEVT["clientX"]-要素位置["left"] YはEVT["clientY"]-要素位置["top"] もし、X<0またはX≧描画中キャンバス["width"]またはY<0またはY≧描画中キャンバス["height"]ならば、 見上は(-3) 戻る ここまで もし、Y<描画中キャンバス["height"]*0.1ならば、 見上は1 違えばもし、Y<描画中キャンバス["height"]*0.3ならば、 # 戻さない範囲 見上は0 違えば、 見上は(-3) ここまで ここまで ●(XYを)狙撃とは NXはXY[0]/描画中キャンバス["width"]*2-1 NYはXY[1]/描画中キャンバス["height"]*(-2)+1 レイキャスタでカメラから[NX,NY]にTJSカメラ起点レイ 位置はレイキャスタのTJSレイ原点取得 方向はレイキャスタのTJSレイ方向取得 位置から方向に投擲する ここまで ●(位置から方向に)投擲とは もし、(石待機リストの配列要素数)が0ならば、 戻る ここまで 石は石待機リストから配列ポップ 鋼体は石["鋼体"] 起点は方向のTJSVec3 起点に位置をTJS加算 ワールドマトリクスワークにAMO単位行列設定 ワールドマトリクスワークに起点をAMO原点設定 鋼体にワールドマトリクスワークをAMOワールド変換行列設定 状態管理器は鋼体からAMO状態管理器取得 もし、状態管理器≠NULLならば、 状態管理器にワールドマトリクスワークをAMOワールド変換行列設定 鋼体をAMO力静止 射出速度は方向のTJSVec3 射出速度に腕力をTJSスカラー乗算 鋼体に射出速度をAMO移動速度設定 Iは鋼体からAMOユーザインデックス取得 情報は鋼体情報[I] 情報["待機中"]はオフ 鋼体をAMO活性化 違えば、 # 「状態管理器がありません」を表示 ここまで ここまで ●ユニット準備処理とは 配置情報は{ ユニット名:「兵士」, 原点: [-28, 2.1, 20], 位置: [-1,2.1,-45], 向: 0, 行動パターン:「右標準」 } 配置情報でユニット追加配置 配置情報は{ ユニット名:「歩兵」, 原点: [-24, 2.1, 20], 位置: [1,2.1,-40], 向: 0, 行動パターン:「左標準」 } 配置情報でユニット追加配置 配置情報は{ ユニット名:「ボット」, 原点: [-20, 2.1, 20], 位置: [1,2.1,-43], 向: 0, 行動パターン:「右挟角」 } 配置情報でユニット追加配置 配置情報は{ ユニット名:「ボット」, 原点: [-16, 2.1, 20], 位置: [1,2.1,-48], 向: 0, 行動パターン:「左挟角」 } 配置情報でユニット追加配置 ここまで ●ユニット初期配置とは 敵リストを反復する 敵は対象 敵["行動"]は「出現」 敵["行動時間"]は0 ここまで ここまで ●(配置情報で)ユニット追加配置とは ユニット名は配置情報["ユニット名"] ユニットはユニットシリーズリスト[ユニット名] モデル名はユニット["モデル名"] モデルはモデルリスト[モデル名] モデルデータはモデル["モデル"]をTJSスケルトン付複製 モデルデータを配置情報["原点"]にTJS位置設定 ココにモデルデータをTJS登場 アニメーション情報はモデル["アニメ"] 形状はモデル["鋼体"]で敵形状作成 形状に衝突マージンをAMOマージン設定 鋼体はモデルデータから形状の敵重量でMESH付鋼体作成 鋼体に4をAMO活性状態設定 鋼体に0.1でAMO反発係数設定 鋼体に敵摩擦係数でAMO回転摩擦係数設定 鋼体に敵摩擦係数でAMO摩擦係数設定 変動物リストに{表示体:モデルデータ, 鋼体:鋼体}を配列追加する 物理ワールドに鋼体をAMO鋼体追加 鋼体に0をAMO回転係数設定 敵ミキサーはモデルデータのTJSアニメーションミキサー作成 敵アクションは{} モデル["アニメマップ"]を反復する アニメ名は対象キー クリップ名は対象 クリップはアニメーション情報[クリップ名] もし、クリップならば、 敵アクション[アニメ名]は敵ミキサーからクリップのTJSクリップアクション取得 ここまで ここまで 敵アクションを反復する アクションは対象 アクションをTJS有効化 アクションに1をTJSアクション再生時間倍率設定 アクションに0をTJSアクション比重設定 アクションをTJSアクション再生 ここまで # 敵アクション["待機"]に1をTJSアクション比重設定 # 行動リストは行動パターン[配置情報["行動パターン"]]を配列複製 鋼体インデックスは鋼体インデックス+1 Iは鋼体インデックス 敵は{ ユニット名: ユニット名, モデル名: モデル名, モデル: モデルデータ, パターン名: 配置情報["行動パターン"], 鋼体: 鋼体, 初期位置: 配置情報["位置"], 初期向: 配置情報["向"], 鋼体インデックス: I, 行動リスト: [], 行動: 「退場」, 行動時間: 0, 向: 0, クリップ名: 空, アクション: 敵アクション, アクションミキサ: 敵ミキサー } 敵リストに敵を配列追加 情報は{ 種類: 「敵」, 表示体: モデルデータ, 鋼体: 鋼体, 衝突済: オフ, 原点: 配置情報["原点"], 待機中: オン, 破壊可能: オン, データ: 敵 } 鋼体情報[I]は情報 鋼体にIをAMOユーザインデックス設定 ここまで ●(敵を)ユニット出現とは 鋼体は敵["鋼体"] ワールドマトリクスワークにAMO単位行列設定 ワールドマトリクスワークに敵["初期位置"]をAMO原点設定 鋼体にワールドマトリクスワークをAMOワールド変換行列設定 状態管理器は鋼体からAMO状態管理器取得 もし、状態管理器≠NULLならば、 状態管理器にワールドマトリクスワークをAMOワールド変換行列設定 ここまで 敵["向"]は敵["初期向"] 敵["行動リスト"]は行動パターン[敵["パターン名"]]を配列複製 敵["行動"]は「待機」 敵["行動時間"]は0 敵["クリップ名"]は「待機」 Iは敵["鋼体インデックス"] 情報は鋼体情報[I] 情報["衝突済"]はオフ 情報["待機中"]はオフ ここまで ●モデル読込完了チェックとは モデル読込数はモデル読込数+1 もし、モデル読込数<モデル総数ならば、 戻る ここまで ユニット準備処理 事前準備完了チェック ここまで ●事前準備完了チェックとは 事前準備完了数は事前準備完了数+1 もし、事前準備完了数<事前常備項目数ならば、 戻る ここまで 全準備完了 ここまで ●全準備完了とは ユニット初期配置 ここまで ●(モデルリストを)モデル読み込むとは モデル総数は0 モデル読込数は0 モデルリストを反復する モデルは対象 モデル総数はモデル総数+1 Pは{PATH: モデルベースURL, URL: モデル["URL"]}をTJSGLTF保障読込 Fはモデルを内包化には(モデル,GLTF) モデルデータはGLTF["scene"] もし、モデル["回転補正"]ならば、 モデルデータ["children"]を反復する OBJは対象 OBJ["rotation"]["x"]はOBJ["rotation"]["x"]+モデル["回転補正"]["x"] OBJ["rotation"]["y"]はOBJ["rotation"]["y"]+モデル["回転補正"]["y"] OBJ["rotation"]["z"]はOBJ["rotation"]["z"]+モデル["回転補正"]["z"] ここまで ここまで もし、モデル["位置補正"]ならば、 モデルデータ["children"]を反復する OBJは対象 OBJをモデル["位置補正"]["軸"]でモデル["位置補正"]["値"]だけTJS移動 ここまで ここまで もし、モデル["拡大"]ならば、 モデルデータ["scale"]["x"]はモデル["拡大"][0] モデルデータ["scale"]["y"]はモデル["拡大"][1] モデルデータ["scale"]["z"]はモデル["拡大"][2] ここまで モデルデータをTJSトラバースには(OBJ) もし、OBJ["isMesh"]ならば、 OBJにオンをTJS影受設定 OBJにオンをTJS影投設定 ここまで ここまで モデル["モデル"]はモデルデータ モデル["アニメ"]は{} GLTF["animations"]を反復する クリップは対象 モデル["アニメ"][クリップ["name"]]はクリップ ここまで モデル読込完了チェック ここまで FをPの成功した時 ここまで ここまで ●(WHをHMINからHMAXで)高低地形データ作成とは データ数はWH[0]×WH[1] データはデータ数のF32配列 差異はHMAX-HMIN W2はWH[0]/2 H2はWH[1]/2 位相は地形位相 Kは0 (WH[1])回繰り返す Jは回数-1 (WH[0])回繰り返す Iは回数-1 半径はSQRT(POW((I-W2)/W2,2)+POW((J-H2)/H2,2)) 高さは(SIN(半径*位相)+1)*0.5*差異+HMIN # 高さは0 データ[K]は高さ KはK+1 ここまで ここまで データで戻る ここまで # 床表現に従い地表を表示する際に使用する素材(Material)を準備して返す # テクスチャの表示数を得るため床分割数を参照している ●地表素材取得とは 縦は床辺分割数 横は床辺分割数 テクスチャはNULL もし、床表現が「芝生」ならば、 テクスチャは{URL:芝生URL}をTJSテクスチャ読込 違えばもし、床表現が「グリッド」ならば、 テクスチャは{URL:グリッドURL}をTJSテクスチャ読込 ここまで もし、テクスチャ≠NULLならば、 テクスチャの["repeat"]に[横,縦]をTJSVec2設定 テクスチャ["wrapS"]はTHREE["RepeatWrapping"] テクスチャ["wrapT"]はTHREE["RepeatWrapping"] 素材は{map: テクスチャ}のTJS拡散反射材質作成 違えば、 素材は{color: 0x408040}のTJS拡散反射材質作成 ここまで 素材で戻る ここまで # 平坦な地面を生成する # ●平地設定とは 縦は床辺分割数 横は床辺分割数 # 地面の見た目 地形は{"幅":床辺長,"高さ":床辺長,横分割数:横,縦分割数:縦}のTJS板作成 地面素材は地表素材取得 地面は地形と地面素材のTJSメッシュ作成 地面["rotation"]["x"] = -PI/2 地面にオンをTJS影受設定 地面をココにTJS登場 # 地面の物理エンジン空間の実体 形状は[床辺長/2,床厚/2,床辺長/2]でAMO箱形状作成 形状に衝突マージンをAMOマージン設定 ワールドマトリクスワークをAMO単位行列設定 ワールドマトリクスワークに[0, 0-床厚/2, 0]をAMO原点設定 鋼体はワールドマトリクスワークから形状の0でMESH無鋼体作成 鋼体に床反発率でAMO反発係数設定 鋼体に床回転摩擦係数でAMO回転摩擦係数設定 鋼体に床摩擦係数でAMO摩擦係数設定 物理ワールドに鋼体をAMO鋼体追加 床鋼体は鋼体 ここまで # 高低のある地面を生成する # ●地表設定とは 高値は地形高値 低値は地形低値 縦は床辺分割数 横は床辺分割数 幅は床辺長/2 奥行は床辺長/2 データは[横,縦]を低値から高値で高低地形データ作成 地形は{"幅":幅,"高さ":奥行,横分割数:横-1,縦分割数:縦-1}のTJS板作成 地形を"X"で(-PI/2)だけTJS回転 頂点は地形["attributes"]["position"]["array"] Jは0 Lは(頂点の配列要素数)/3 (L)回繰り返す Iは回数-1 頂点[J+1]はデータ[I] JはJ+3 ここまで 地形のTJS法線計算 地面素材は地表素材取得 地面は地形と地面素材のTJSメッシュ作成 地面にオンをTJS影受設定 地面をココにTJS登場 形状は{横:横,縦:縦,最高:高値,最低:低値,上方向:"Y",データ:データ}でAMO高度マップ形状作成 形状に([幅/(横-1), 1, 奥行/(縦-1)]からAMOVec3)をAMO局所スケール設定 形状に0.1をAMOマージン設定 ワールドマトリクスワークをAMO単位行列設定 ワールドマトリクスワークに[0,(高値+低値)/2,0]をAMO原点設定 鋼体はワールドマトリクスワークから形状の0でMESH無鋼体作成 鋼体に床反発率でAMO反発係数設定 鋼体に床回転摩擦係数でAMO回転摩擦係数設定 鋼体に床摩擦係数でAMO摩擦係数設定 物理ワールドに鋼体をAMO鋼体追加 床鋼体は鋼体 ここまで ●(WHDで)敵形状作成とは 形状はAMO複合形状作成 脚部径はMIN(WHD[0],WHD[2]) 直方体は[WHD[0], WHD[1]-脚部径/2, WHD[2]]でAMO箱形状作成 ワールドマトリクスワークをAMO単位行列設定 ワールドマトリクスワークに[0,0+(脚部径/2),0]をAMO原点設定 形状にワールドマトリクスワークで直方体をAMO子形状追加 球は脚部径でAMO球形状作成 ワールドマトリクスワークをAMO単位行列設定 ワールドマトリクスワークに[0,0-(WHD[1]-脚部径),0]をAMO原点設定 形状にワールドマトリクスワークで球をAMO子形状追加 形状で戻る ここまで # 基本単位に従いものを配置する。 # この世界では、長さはメートル、重さはキログラム、時間は秒で指定する。 # ただし、あまり細かい数値はうまく扱えない。 ●描画準備とは # 物理エンジンの空間(力学ワールド)を準備 # 物理ワールドは標準鋼体ワールド作成 # 表示するためのシーンを準備 # ココはTJSシーン作成 ココに0x000000をTJS背景設定 # 霧発生がオンならば、霧を生成して設定する # もし、霧発生ならば、 霧は0xe0e0e0で5から50までのTJSフォッグ作成 ココに霧をTJS霧効果設定 ここまで # カメラを準備 # カメラは、[60,WEBGLキャンバス["width"]/WEBGLキャンバス["height"],0.1,200]のTJS透視投影カメラ作成 カメラを[0, 2, 0]にTJS位置設定 カメラを[0, 2, -50]にTJS視点設定 カメラを"+Y"にTJSカメラ上方設定 ココにカメラをTJS登場 カメラのTJS投影マトリクス更新 # 地面を準備 # もし、地形平地ならば、 平地設定 違えば、 地表設定 ここまで # 光源を準備 # # 全体をぼやっと照らす環境光源を生成 光は0x101010のTJS環境光源作成 ココに光をTJS登場 # 1点から射程内を照らすポイント光源を生成 ランタンは[0xc0c0c0, 2, 200, 1.0]のTJS点光源作成 ランタンを[20, 100, 0]にTJS位置設定 ランタンにオンをTJS影投設定 ココにランタンをTJS登場 ランタンの["シャドー"]に[1024,1024]をTJSマップサイズ設定 ランタンの["シャドー","カメラ"]に0.1をTJSカメラ最近距離設定 ランタンの["シャドー","カメラ"]に200をTJSカメラ最遠距離設定 # 投石用の石をあらかじめ作成しておく # 石準備処理 # ThreeJSの表示準備 # レンダラは'three_cv'にTJS描画準備 レンダラに[WEBGLキャンバス["width"],WEBGLキャンバス["height"]]をTJSサイズ設定 レンダラに0x000000をTJSクリア色設定 レンダラをTJS影処理有効 もし、オービタルコントローラならば、 # DOMには採取的に表示に使うキャンバスを指定する コントローラはカメラに描画中キャンバスのTJS衛星軌道コントローラ作成 ここまで 0でアニメート ここまで ●石準備処理とは # 投石用の石を後ろの見えないところに置いておく # # 石の形状と素材を準備 石形状は{半径:石半径, 横分割数:8, 縦分割数:8, バッファ:オン}のTJS球体作成 石素材は{ color: 0xcccccc }のTJS拡散反射材質作成 形状は石半径のAMO球形状作成 形状に衝突マージンをAMOマージン設定 # 石を指定個数作り出して、後ろの置いておく (石個数)回繰り返す 石は石形状と石素材のTJSメッシュ作成 Xは(回数-1-(石個数/2))*(石半径*2+0.2) Yは床厚/2+石半径+0.2 Zは10 石を[X,Y,Z]にTJS位置設定 石にオンをTJS影投設定 ココに石をTJS登場 鋼体は石から形状の石重量でMESH付鋼体作成 鋼体に石反発率でAMO反発係数設定 鋼体に弾回転摩擦係数でAMO回転摩擦係数設定 鋼体に弾摩擦係数でAMO摩擦係数設定 変動物リストに{表示体:石, 鋼体:鋼体}を配列追加する 物理ワールドに鋼体をAMO鋼体追加 情報は{ 種類: 「弾」, 表示体: 石, 鋼体: 鋼体, 原点: [X,Y,Z], 待機中: オン, 衝突済: オフ, 破壊可能: オン, データ: NULL } 鋼体インデックスは鋼体インデックス+1 Iは鋼体インデックス 鋼体情報[I]は情報 石待機リストに{鋼体:鋼体}を配列追加 鋼体にIをAMOユーザインデックス設定 ここまで ここまで # アニメーションのループを構成する # 呼び出しごとの時間差の算出もここで行う ●(タイムスタンプで)アニメートとは もし、前回タイムスタンプが0でなければ、 経過秒は(タイムスタンプ-前回タイムスタンプ)÷1000 経過秒でワンフレーム ここまで 前回タイムスタンプはタイムスタンプ 画面更新時実行には(タイムスタンプ) タイムスタンプでアニメート ここまで ここまで # アニメーションの1フレーム分を処理する ●(経過秒で)ワンフレームとは # 力学ワールドの時間を経過時間分進める 物理ワールドを経過秒で10までAMO時間経過 経過秒で敵軍処理する # 力学ワールド上の鋼体の位置・姿勢を3Dモデル空間のモデルに反映する 変動物リストを反復する 表示体は対象["表示体"] 鋼体は対象["鋼体"] 鋼体を表示体に物体姿勢反映 Iは鋼体からAMOユーザインデックス取得 情報は鋼体情報[I] もし、情報["種類"]=「弾」かつ情報["待機"]=オフならば、 もし、鋼体がAMO活性でなければ、 鋼体を原点待機 石待機リストに{鋼体:鋼体}を配列追加 ここまで ここまで ここまで # 操作に従いカメラの姿勢を変更する もし、見上≠0ならば、 Xはカメラ["rotation"]["x"]+見上*見上速度 もし、見上>0ならば、 もし、X>PI/2ならば、 XはPI/2 ここまで 違えば、 もし、X<0ならば、 Xは0 ここまで ここまで カメラ["rotation"]["x"]はX ここまで 衝突判定する レンダラにココをカメラでTJS描画 WEBGLキャンバスを[0,0]へ画像描画 ここまで # 鋼体同士の衝突を調べて、処理する。 # 少なくとも一方が破壊可能(=敵/弾)な場合のみ処理する # ただし、両方敵の場合は処理しない ●衝突判定とは Nは物理ワールドからAMO多様体数取得 (N)回繰り返す Iは回数-1 多様体は物理ワールドからIのAMO多様体取得 鋼体Aは(多様体からAMO衝突オブジェクトA取得)を"btRigidBody"にAMOCAST 鋼体Bは(多様体からAMO衝突オブジェクトB取得)を"btRigidBody"にAMOCAST Iは鋼体AからAMOユーザインデックス取得 鋼体情報Aは鋼体情報[I] Iは鋼体BからAMOユーザインデックス取得 鋼体情報Bは鋼体情報[I] もし、(鋼体情報A=NULL)かつ(鋼体情報B=NULL)ならば、 続ける ここまで もし、鋼体情報AがNULLならば、 鋼体情報Aは{ 衝突済: オフ, 種類: "他", 破壊可能: オフ, 待機中: オフ } ここまで もし、鋼体情報BがNULLならば、 鋼体情報Bは{ 衝突済: オフ, 種類: "他", 破壊可能: オフ, 待機中: オフ } ここまで もし、鋼体情報A["衝突済"]かつ鋼体情報B["衝突済"]ならば、 続ける ここまで # 敵同時、敵と床は衝突しない もし、鋼体情報A["種類"]=「敵」かつ鋼体情報B["種類"]=「他」ならば、 続ける ここまで もし、鋼体情報A["種類"]=「他」かつ鋼体情報B["種類"]=「敵」ならば、 続ける ここまで もし、鋼体情報A["種類"]=「敵」かつ鋼体情報B["種類"]=「敵」ならば、 続ける ここまで もし、(鋼体情報A["待機中"])||(鋼体情報B["待機中"])ならば、 続ける ここまで もし、(鋼体情報A["破壊可能"])||(鋼体情報B["破壊可能"])ならば 衝突はオフ 最大衝撃は0 Mは多様体からAMO近接点数取得 (M)回繰り返す Jは回数-1 点は多様体からJのAMO近接点取得 もし、(点からAMO近接距離取得)<0ならば、 衝突はオン 力は点からAMO衝突力取得 もし、力>最大衝撃ならば、 最大衝撃は力 ここまで ここまで ここまで もし、衝突でなければ、 続ける ここまで もし、(最大衝撃<25)&&((鋼体情報A["種類"]=「敵」)||(鋼体情報B["種類"]=「敵」))ならば、 続ける ここまで もし、鋼体情報A["破壊可能"]ならば、 鋼体情報A["衝突済"]はオン 鋼体Aを原点待機 もし、鋼体情報A["種類"]が「敵」ならば、 鋼体Aを敵破壊 違えばもし、鋼体情報A["種類"]が「弾」ならば、 石待機リストに{鋼体:鋼体A}を配列追加 ここまで ここまで もし、鋼体情報B["破壊可能"]ならば、 鋼体情報B["衝突済"]はオン 鋼体Bを原点待機 もし、鋼体情報B["種類"]が「敵」ならば、 鋼体Bを敵破壊 違えばもし、鋼体情報B["種類"]が「弾」ならば、 石待機リストに{鋼体:鋼体B}を配列追加 ここまで ここまで ここまで ここまで ここまで ●(鋼体を)敵破壊とは Iは鋼体からAMOユーザインデックス取得 情報は鋼体情報[I] もし、情報["種類"]=「敵」ならば、 敵は情報["データ"] 敵["行動リスト"]は空配列 もし、討伐時自動再出現ならば、 敵["行動"]は「出現」 敵["行動時間"]は(5の乱数)+(5の乱数) 違えば、 敵["行動"]は「退場」 敵["行動時間"]は300 ここまで ここまで ここまで ●(鋼体を)原点待機とは Iは鋼体からAMOユーザインデックス取得 情報は鋼体情報[I] ワールドマトリクスワークにAMO単位行列設定 ワールドマトリクスワークに情報["原点"]をAMO原点設定 鋼体にワールドマトリクスワークをAMOワールド変換行列設定 姿勢状態は鋼体のAMO状態管理器取得 姿勢状態にワールドマトリクスワークをAMOワールド変換行列設定 鋼体をAMO力静止 鋼体に[0, 0, 0]をAMO移動速度設定 鋼体に[0, 0, 0]をAMO回転速度設定 情報["待機中"]はオン ここまで ●(経過秒で)敵軍処理とは 敵リストを反復する 敵は対象 もし、敵["行動"]が「退場」ならば、 続ける ここまで もし、敵["行動時間"]>0ならば、 敵["行動時間"]=敵["行動時間"]-経過秒 ここまで もし、敵["行動時間"]≦0ならば、 もし、(敵["行動リスト"]の配列要素数)>0ならば、 次行動は敵["行動リスト"][0] 敵["行動リスト"]の0を配列切取 敵["行動"]は次行動[0] 敵["行動時間"]は次行動[1] 違えば、 もし、敵["行動"]が「出現」でなければ、 もし、行動終了時自動再出現ならば、 敵["行動リスト"]は[["待機",3],["出現",0]] 敵["行動"]は「待機」 敵["行動時間"]は0 違えば、 敵["行動"]は「待機」 敵["行動時間"]は300 ここまで ここまで ここまで ここまで もし、敵["行動"]が「出現」ならば、 敵をユニット出現 ここまで 敵を経過秒だけ敵行動 敵["アクション"]を反復する アニメ名は対象キー アクションは対象 もし、アニメ名=敵["クリップ名"]ならば、 アクションに1をTJSアクション比重設定 違えば、 アクションに0をTJSアクション比重設定 ここまで ここまで 敵["アクションミキサ"]を経過秒でTJS更新 ここまで ここまで ●(敵を経過時間だけ)敵行動とは 行動は敵["行動"] 鋼体は敵["鋼体"] マトリクスはAMO変形マトリクス作成 姿勢状態は鋼体のAMO状態管理器取得 姿勢状態からマトリクスにAMOワールド変換行列取得 回頭速さは経過時間×敵回頭速度 回頭係数は0 行動で条件分岐する 「左回転」ならば、 回頭係数は1 ここまで 「左旋回」ならば、 回頭係数は1 ここまで 「右回転」ならば、 回頭係数は-1 ここまで 「右旋回」ならば、 回頭係数は-1 ここまで ここまで # 向きは管理情報に反映する 敵["向"]は敵["向"]+回頭速さ×回頭係数 敵["向"]は敵["向"]を(2*PI)で割った余り 回転はNULLからAMO四元数 # 移動時の前方取得用に地面の法線に対して回転したマトリクスを求める # 地面が重力に直角な平面ならY軸に直角な平面と同一になるので算出不要 姿勢状態からワールドマトリクスワークにAMOワールド変換行列取得 上方向は(ワールドマトリクスワークからAMO基礎取得)から1のAMO行取得 上方向にAMOアクセッサ設定 下方向は[(-上方向["x"]),(-上方向["y"]),(-上方向["z"])]からAMOVec3 下方向をAMO正規化 起点はワールドマトリクスワークからAMO原点取得 対象は起点のAMOVec3 下方向に(2*1.1)をAMO乗算 対象に下方向をAMO加算 下方向をAMO破棄 処理はAMOレイ最近接保持処理作成 物理ワールドを起点から対象に処理でAMOレイ試行 距離は1 法線はNULL もし、処理のAMOレイヒットならば、 距離は処理["m_closestHitFraction"] 法線は処理["m_hitNormalWorld"] # 法線Tは法線のTJSVec3 # 法線Tを表示 # 「ヒット:{距離}:({法線T["x"]},{法線T["y"]},{法線T["z"]})」を表示 ここまで 起点をAMO破棄 対象をAMO破棄 処理をAMO破棄 もし、法線=NULLならば、 法線は上方向のAMOVec3 法線をAMO正規化 ここまで # 敵は2脚なので常にY軸に対しての回転を行う(重力に対して垂直) 回転に[[0,1,0],敵["向"]]をAMO回転設定 マトリクスに回転をAMO回転設定 # 移動時の前方取得用に地面の法線に対して回転したマトリクスを求める # 4脚や4輪等の場合は敵にもこのマトリクスを適用する(地面に対して垂直) 回転に[法線,敵["向"]]をAMO回転設定 ワールドマトリクスワークに回転をAMO回転設定 回転をAMO破棄 上方向をAMO破棄 法線をAMO破棄 移動速度は鋼体からAMO移動速度取得 速さは鋼体からAMO速さ取得 前方は(ワールドマトリクスワークからAMO基礎取得)から2のAMO行取得 前方にAMOアクセッサ設定 前方をAMO正規化 前方["z"]は(-前方["z"]) 歩行方向は[0,0,0]からAMOVec3 移動速さは0 最大移動速さは0 行動で条件分岐する 「走行」ならば、 前後は「前」 歩調は「走」 ここまで 「前進」ならば、 前後は「前」 歩調は「歩」 ここまで 「歩行」ならば、 前後は「前」 歩調は「歩」 ここまで 「右旋回」ならば、 前後は「前」 歩調は「歩」 ここまで 「左旋回」ならば、 前後は「前」 歩調は「歩」 ここまで 「後退」ならば、 前後は「後」 歩調は「歩」 ここまで ここまで 歩調で条件分岐する 「歩」ならば、 移動速さは経過時間×敵歩行速さ 最大移動速さは敵最大歩行速さ ここまで 「走」ならば、 移動速さは経過時間×敵走行速さ 最大移動速さは敵最大走行速さ ここまで ここまで 前後で条件分岐する 「前」ならば、 歩行方向に前方をAMO減算 ここまで 「後」ならば、 歩行方向に前方をAMO加算 ここまで ここまで 前方をAMO破棄 # 移動は鋼体の移動速度に反映する もし、慣性有ならば、 もし、速さ>(最大移動速さ*1.5)ならば、 移動速度に0.2をAMO乗算 鋼体に移動速度をAMO移動速度設定 違えばもし、速さ<最大移動速さならば、 歩行方向に移動速さをAMO乗算 移動速度に歩行方向をAMO加算 鋼体に移動速度をAMO移動速度設定 ここまで 違えば、 もし、速さ>(最大移動速さ*1.5)ならば、 速さ=速さ×0.8 違えばもし、速さ<最大移動速さならば、 速さ=速さ+移動速さ もし、速さ>最大移動速さならば、 速さは最大移動速さ ここまで ここまで 歩行方向に速さをAMO乗算 # Y軸の速度のみコピーして、重力による落下の加速を打ち消さないようにする 移動速度にAMOアクセッサ設定 歩行方向にAMOアクセッサ設定 歩行方向["y"]は移動速度["y"] 鋼体に歩行方向をAMO移動速度設定 ここまで # 現在の速さを元に完全停止かどうかの切り分けと表示に使用するクリップ名を求める 速さは鋼体からAMO速さ取得 もし、速さ<0.05ならば、 敵["クリップ名"]は「待機」 鋼体に[0,0,0]をAMO移動速度設定 違えばもし、速さ<敵最大歩行速さならば、 敵["クリップ名"]は「歩行」 違えば、 敵["クリップ名"]は「走行」 ここまで 歩行方向をAMO破棄 移動速度をAMO破棄 姿勢状態にマトリクスをAMOワールド変換行列設定 鋼体にマトリクスをAMO重心ワールド変換行列設定 マトリクスをAMO破棄 ここまで ●開始とは ライブラリ準備 ここまで # ThreeJS - Ammo 利用簡易支援 # ThreeJSのexamples/js/physics/AmmoPhysics.jsを参考に作成 # よくある組み合わせで力学ワールドを生成する ●標準鋼体ワールド作成とは CONFはAMOデフォルト衝突判定法作成 DISPはCONFのAMO衝突判定ディスパッチャ作成 起点は[-250,-250,-250]からAMOVec3 終点は[250,250,200]からAMOVec3 BPHASEは起点から終点でAMO三軸スィープ作成 起点をAMO破棄 終点をAMO破棄 SOLVERはAMO物理ソルバー作成 ワールドは[DISP,BPHASE,SOLVER,CONF]でAMO離散力学ワールド作成 ワールドに[0,(-重力),0]でAMO重力設定 ワールドマトリクスワークはAMO変形マトリクス作成 ワールドで戻る ここまで # AmmoJSの鋼体の位置・姿勢をThreeJSのMESHに反映する ●(BODYをMESHに)物体姿勢反映とは 姿勢状態はBODYのAMO状態管理器取得 姿勢状態からワールドマトリクスワークにAMOワールド変換行列取得 位置はワールドマトリクスワークからAMO原点取得 回転はワールドマトリクスワークからAMO回転取得 MESHを位置にTJS位置設定 MESHを回転にTJS四元数設定 位置をAMO破棄 回転をAMO破棄 ここまで # ThreeJSのMESHを情報を元にAmmoJSの鋼体を作成する ●(MESHからSHAPEのMASSで)MESH付鋼体作成とは 位置はMESHからTJS位置取得 回転はMESHからTJS四元数取得 ワールドマトリクスワークをAMO単位行列設定 ワールドマトリクスワークに位置をAMO原点設定 ワールドマトリクスワークに回転をAMO回転設定 BODYはワールドマトリクスワークからSHAPEのMASSでMESH無鋼体作成 BODYで戻る ここまで # 変換マトリクスから鋼体を作成する ●(マトリクスからSHAPEのMASSで)MESH無鋼体作成とは もし、MASSでなければ、 MASSは0 ここまで 状態管理器はマトリクスからAMOデフォルト状態管理器作成 慣性は[0,0,0]からAMOVec3 SHAPEにMASSと慣性でAMO局所慣性計算 構成情報は[MASS, 状態管理器, SHAPE, 慣性]からAMO鋼体構成情報作成 慣性をAMO破棄 BODYは構成情報からAMO鋼体作成 BODYで戻る ここまで # 型付配列のヘルパー命令 ●(ARGの)UI8配列とは 『(function (a) {return new Uint8Array(a)})』を[ARG]でJS関数実行で戻る ここまで ●(ARGの)F32配列とは 『(function (a) {return new Float32Array(a)})』を[ARG]でJS関数実行で戻る ここまで ●(AをBで)POWとは 『(function(a,b){return Math.pow(a,b)})』を[A,B]でJS関数実行で戻る ここまで ●(AとBの)MINとは もし、A<Bならば、 Aで戻る ここまで Bで戻る ここまで ●(FにUSERDATAを)内包化とは 『(function(func, data) { return function(...args) { return func(data,...args); } })』を[F,USERDATA]でJS関数実行で戻る ここまで ●(DOMから)DOMクライアント境界取得とは DOMの"getBoundingClientRect"を[]でJSメソッド実行で戻る ここまで ここまで