モデルの当たり判定
[最終更新日:2019/11/07]
モデル自体には当たり判定を行う機能はありませんが、
アートメッシュの頂点情報を取得できることを利用して当たり判定処理を実装することができます。
ここで述べる”当たり判定”とは、
画面上の指定のポイントに対象のメッシュがあるかどうかのテストのことを指します。
当たり判定の用意
当たり判定にはアートメッシュを利用します。
Editor上での当たり判定用のアートメッシュの用意は「Editor上での当たり判定の設定」を確認してください。
当たり判定を用意した後、Cubism Viewer (for OW)の機能で当たり判定を設定して.model3.jsonファイルに組み込む必要があります。
Cubism Viewer (for OW)上で当たり判定を設定して出力すると、.model3.jsonファイルには以下のように記述されます。
1 2 3 4 5 6 7 8 9 |
{ ... "HitAreas": [ { "Id": "HitArea", "Name": "Body" } ] } |
Cubism Viewer (for OW)ではHitArea~とIDが設定されたアートメッシュしか登録できませんが、
.model3.jsonファイルをテキストエディタで直接加工すればHitArea~以外のアートメッシュでも当たり判定に設定できます。
当たり判定を行う
当たり判定にはNative(C++)のCubismUserModel::IsHit関数、またはWeb(TypeScript)のCubismUserModel.isHit関数が利用できます。
第一引数で対象のdrawableのID、第二、第三引数で検査する描画上のX、Yの値を指定します。
1 2 3 4 5 6 7 |
// C++ CubismUserModel model; CubismIdHandle drawId = CubismFramework::GetIdManager()->GetId("HitArea1"); if(model.IsHit(drawId, x, y)) { //処理 } |
1 2 3 4 5 6 7 |
// TypeScript let model: CubismUserModel; let drawId: CubismIdHandle = CubismFramework.getIdManager().getId("HitArea1"); if(mode.isHit(drawId, x, y)) { // 処理 } |
当たり判定の内容と注意点
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
// C++csmBool CubismUserModel::IsHit(CubismIdHandle drawableId, csmFloat32 pointX, csmFloat32 pointY){ csmInt32 drawIndex = _model->GetDrawableIndex(drawableId); if (drawIndex < 0) { return false; // 存在しない場合はfalse } csmInt32 count = _model->GetDrawableVertexCount(drawIndex); const csmFloat32* vertices = _model->GetDrawableVertices(drawIndex); csmFloat32 left = _model->GetCanvasWidth(); csmFloat32 right = 0.0f; csmFloat32 top = _model->GetCanvasHeight(); csmFloat32 bottom = 0.0f; for (csmInt32 j = 0; j < count; ++j) { csmFloat32 x = vertices[Constant::VertexOffset + j * Constant::VertexStep]; csmFloat32 y = vertices[Constant::VertexOffset + j * Constant::VertexStep + 1]; if (x < left) { left = x; // Min x } if (x > right) { right = x; // Max x } if (y < top) { top = y; // Min y } if (y > bottom) { bottom = y; // Max y } } csmFloat32 tx = _modelMatrix->InvertTransformX(pointX); csmFloat32 ty = _modelMatrix->InvertTransformY(pointY); return ((left <= tx) && (tx <= right) && (top <= ty) && (ty <= bottom));}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
// TypeScriptpublic isHit(drawableId: CubismIdHandle, pointX: number, pointY: number): boolean{ const drawIndex: number = this._model.getDrawableIndex(drawableId); if(drawIndex < 0) { return false; // 存在しない場合はfalse } const count: number = this._model.getDrawableVertexCount(drawIndex); const vertices: Float32Array = this._model.getDrawableVertices(drawIndex); let left: number = vertices[0]; let right: number = vertices[0]; let top: number = vertices[1]; let bottom: number = vertices[1]; for(let j: number = 1; j < count; ++j) { let x = vertices[Constant.vertexOffset + j * Constant.vertexStep]; let y = vertices[Constant.vertexOffset + j * Constant.vertexStep + 1]; if(x < left) { left = x; // Min x } if(x > right) { right = x; // Max x } if(y < top) { top = y; // Min y } if(y > bottom) { bottom = y; // Max y } } const tx: number = this._modelMatrix.invertTransformX(pointX); const ty: number = this._modelMatrix.invertTransformY(pointY); return ((left <= tx) && (tx <= right) && (top <= ty) && (ty <= bottom));}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
// C++ csmBool CubismUserModel::IsHit(CubismIdHandle drawableId, csmFloat32 pointX, csmFloat32 pointY) { csmInt32 drawIndex = _model->GetDrawableIndex(drawableId); if (drawIndex < 0) { return false; // 存在しない場合はfalse } csmInt32 count = _model->GetDrawableVertexCount(drawIndex); const csmFloat32* vertices = _model->GetDrawableVertices(drawIndex); csmFloat32 left = _model->GetCanvasWidth(); csmFloat32 right = 0.0f; csmFloat32 top = _model->GetCanvasHeight(); csmFloat32 bottom = 0.0f; for (csmInt32 j = 0; j < count; ++j) { csmFloat32 x = vertices[Constant::VertexOffset + j * Constant::VertexStep]; csmFloat32 y = vertices[Constant::VertexOffset + j * Constant::VertexStep + 1]; if (x < left) { left = x; // Min x } if (x > right) { right = x; // Max x } if (y < top) { top = y; // Min y } if (y > bottom) { bottom = y; // Max y } } csmFloat32 tx = _modelMatrix->InvertTransformX(pointX); csmFloat32 ty = _modelMatrix->InvertTransformY(pointY); return ((left <= tx) && (tx <= right) && (top <= ty) && (ty <= bottom)); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
// TypeScript public isHit(drawableId: CubismIdHandle, pointX: number, pointY: number): boolean { const drawIndex: number = this._model.getDrawableIndex(drawableId); if(drawIndex < 0) { return false; // 存在しない場合はfalse } const count: number = this._model.getDrawableVertexCount(drawIndex); const vertices: Float32Array = this._model.getDrawableVertices(drawIndex); let left: number = vertices[0]; let right: number = vertices[0]; let top: number = vertices[1]; let bottom: number = vertices[1]; for(let j: number = 1; j < count; ++j) { let x = vertices[Constant.vertexOffset + j * Constant.vertexStep]; let y = vertices[Constant.vertexOffset + j * Constant.vertexStep + 1]; if(x < left) { left = x; // Min x } if(x > right) { right = x; // Max x } if(y < top) { top = y; // Min y } if(y > bottom) { bottom = y; // Max y } } const tx: number = this._modelMatrix.invertTransformX(pointX); const ty: number = this._modelMatrix.invertTransformY(pointY); return ((left <= tx) && (tx <= right) && (top <= ty) && (ty <= bottom)); } |
当たり判定はアートメッシュの頂点をすべて走査し、頂点のXY座標の最大、最小値から矩形の範囲を作り、
検査対象の画面座標をモデルの変換行列から逆変換し、矩形に収まっているか確認する仕組みです。
この関数をつかう場合、以下の点に注意が必要です。
・モデルの変換座標に回転が入ると正しく変換されない。
・四角形に近いアートメッシュが回転デフォーマなどで回転すると大きくサイズが変わる。