質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.91%

ループを使ったTextGeometryの生成(Three.js)

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 2,245

essex

score 21

お世話になります。Three.jsについて勉強しているのですが、TextGeometryについて
分からないことがあり、質問させていただきます。

現在、表示したい文字と座標を格納した2つのアレイからデータを読み込み、3Dテキストを
配置しようとしています。

作成したのは以下のコード(抜粋です)なのですが、これですと文字が配置されません。

var sText;
var sMaterial;
var textGeo;
var loader = new THREE.FontLoader();
for (var i = 0; i < Name.length; i++)
{    
loader.load( 'fonts/helvetiker_regular.typeface.js', function ( font ) {
textGeo = new THREE.TextGeometry(Name[i], {
font: font,
size: 5,
.
中略
.
});
sMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff } );
sText = new THREE.Mesh( textGeo, sMaterial );
sText.position.set(points[i].position.x,points[i].position.y, points[i].position.z);
text_array.push(sText);
scene.add( sText );
});
}

不思議なのは、以下の様に書くと、正常に配置されることです。
ということは、表示したい文字と座標のデータは、アレイに正常に格納されていると思われます。

var sText;
var sMaterial;
var textGeo;
var loader = new THREE.FontLoader();
loader.load( 'fonts/helvetiker_regular.typeface.js', function ( font ) {
textGeo = new THREE.TextGeometry(Name[0], {
font: font,
size: 5,
.
中略
.
});
sMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff } );
sText = new THREE.Mesh( textGeo, sMaterial );
sText.position.set(points[0].position.x,points[0].position.y, points[0].position.z);
text_array.push(sText);
scene.add( sText );
});

do whileでも駄目でしたので、恐らくループのネストの中に入れてはいけないものを
書いてしまっているのではないかと思うのですが、色々変えてもどこが悪いか特定できません。

http://chatora.lolipop.jp/?p=176

こちらのサイトを参考にさせていただきましたが、ループを使ったTextGeometryの配置は可能なようです。
違うのはThree.jsのバージョンの違いから、フォントデータ(json)をhtmlではなくscriptの中から
読み込んでいる点です。

どこが悪いかご教授下さい。よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • turbgraphics200

    2017/01/20 13:54

    コードは<code>ボタンを使って記入してください。これだと、コードが読みづらいです。

    キャンセル

回答 2

checkベストアンサー

+2

よく陥りやすいコード実装です。
ループ内において非同期で呼び出される関数(今回の場合はloader.load()のコールバック関数)の中で、ループされる変数を使用すると、最後の値で実行されます。これは、ループが先に完了してしまい後から非同期で関数が実行されるためです。
ですので、別関数にして呼び出すか、クロージャーを使用して非同期関数を呼び出します。
それ以前に、フォントパス情報(typeface.js)をロードしていますが、ループ内でやってしまうと何回もロードされてしまいます。これは1回のロードで十分です。

// 先にfontをロードしておく
loader.load( 'fonts/helvetiker_regular.typeface.js', function (font) { 
  // ロードが完了したらTextのMesh生成
  createText(font);
});


function createTxtMesh(font) {
  for (var i = 0; i < Name.length; i++) {
    var txtGeo = new THREE.TextGeometry({ font: font, ... 略});
    // 略
  }
} 

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

配置されないというのは、具体的にどういう状態でしょう?

ただ、ざっと見たところ、ループの変数 i の値が多分、おかしくなっている気がします。
このあたりは非同期処理とループで検索してみるといいかもしれません。

結論から言うと、おそらく、ジオメトリの配置処理の時点ではすべての i が同じ値になっていると思います。
(非同期処理のため、変数 i を参照しても、ループ処理が終わってしまっており、結果としてループ終了後の最後の値が入っている)

なので、以下のようにするとうまく動きませんか?

loader.load( 'fonts/helvetiker_regular.typeface.js', (function (i) {
return function ( font ) { 
    textGeo = new THREE.TextGeometry(Name[i], { 
        font: font, 
        size: 5, 
        . 
        中略 
        . 
    });
    sMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff } ); 
    sText = new THREE.Mesh( textGeo, sMaterial ); 
    sText.position.set(points[i].position.x,points[i].position.y,            points[i].position.z); 
    text_array.push(sText); 
    scene.add( sText );
}
}(i))
});

要は、変数 i をループ処理時点で束縛する、ということをしてます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/21 05:20

    edo_m18様、turbgraphics200様、ご教授ありがとうございます。
    また、状況についての説明が不十分だったり、コードの記述が見にくかったりで、
    申し訳ありません。

    正にご指摘通りの状況でした。要するにループのインデックスだけが先に加算されて、
    その後ループの中の処理が走るので、実際に処理する時にはインデックスが終わりまで
    行っている…という状況だったのですね。勉強になりました。

    そう言われて良く考えると、ループの中で引数が正しく渡されているかwindow.alertで
    1周ごとに値を確認してみた時は、文字が指定した場所に表示されていました。どうして
    window.alertを削除すると正しく表示されなくなるのか不思議でしたが、その理由が
    理解できました。また私が最初にアップしたコードは、文字が配置されていないのかと
    思いましたが、言われてみると、全てのテキストが一カ所に表示されている様に見えます。

    取りあえず、以下の様に直し、期待通りの結果を得ることができました。
    do{
    var dmin = get3DTX(Name[i],position);
    //domain_text.push(dmin);
    //earth.add( dmin );
    i= i + 1;
    }while( i < Name.length);

    function get3DTX(text_string, position)
    {
    var loader = new THREE.FontLoader();
    loader.load( 'fonts/helvetiker_regular.typeface.js', function ( font ) {
    var textGeo = new THREE.TextGeometry(text_string, {
    font: font,
    size: 2,
    height: 0,
    curveSegments: 0,
    bevelThickness: 0,
    bevelSize: 0,
    bevelEnabled: true
    });
    var lsMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff } );
    var lsText = new THREE.Mesh( textGeo, lsMaterial );
    lsText.position.set(position.x,position.y, position.z);
    Name_array.push(lsText);
    scene.add( lsText );
    });
    }
    本当は、生成された3D文字データ(この場合lsText)を戻り値として返し、配置とシーンへの追加は
    戻った後に行おうとしたのですが、3D文字のpositionセットがうまくいかないため、
    positionデータも引数とし、関数の中でset positionしています。

    ところで…

    turbgraphics200様のご指摘の通り、これですとフォントパスのロードが繰り返されてしまいます。
    何故この様な記述になっているかというと、ローダーの引数としてフォントの設定情報を渡す訳ですが、
    その中に実際に表示したい文字の情報が含まれているため、例えば10種類の文字を表示するためには、
    ロードを10回繰り返すしか方法が浮かばなかったのです。

    (このリンクを参考にしました。
    http://stackoverflow.com/questions/35589984/textgeometry-in-three-js

    ネストをもう一段追加して以下の様に書いてみましたが、これは動いてくれません。
    textgeometryに関する処理は、同じWindows7、同じIE11でも、表示される場合とされない場合があるのが
    不思議です。

    そもそも質問した問題点の原因は理解できたので解決とさせていただきますが、
    もし、良い書き方がありましたら、ご教授下さい。

    var loader = new THREE.FontLoader();
    loader.load( 'fonts/helvetiker_regular.typeface.js', function ( font ) {
    createText(font);
    });

    function createTxtMesh(font)
    {
    for (var i = 0; i < Name.length; i++)
    {
    var dmin = get3DTX(top_domain[i],position[i]);
    }
    }

    function get3DTX(text_string, position)
    {
    var textGeo = new THREE.TextGeometry(text_string, {
    font: font,
    size: 2,
    height: 0,
    curveSegments: 0,
    bevelThickness: 0,
    bevelSize: 0,
    bevelEnabled: true
    });
    var lsMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff } );
    var lsText = new THREE.Mesh( textGeo, lsMaterial );
    lsText.position.set(position.x,position.y, position.z);
    domain_text.push(lsText);
    earth.add( lsText );
    return lsText;
    }

    お二人のアドバイスはとても助かりましたので甲乙付けがたかったのですが、
    ローダーの問題点もご指摘いただきましたので、turbgraphics200様のご回答を
    ベストアンサーとさせていただきます。

    非常に助かりました。私一人では、気付かなかったと思います。今後とも
    よろしくお願いします。ありがとうございました。

    キャンセル

  • 2017/01/21 05:23

    また、読みにくいコードをアップしてしまいました。
    済みません。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る