SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ゲームエンジン「ngCore」最速チュートリアル(AD)

「忍者ロワイヤル」を真似してみよう!
~サンプルで学ぶ、ngCoreを使ったゲーム開発

ゲームエンジン「ngCore」最速チュートリアル 第2回

  • このエントリーをはてなブックマークに追加

剣の軌跡を表示しよう

 DeNAがスマートフォン向けに提供しているゲームに「忍者ロワイヤル」というものがあります。「忍者ロワイヤル」はスマートフォンならではの操作がウリのアクションRPGで、特にバトルはスワイプやタップを駆使して遊ぶものとなっています。今回はそのバトルの中の一要素である“剣の軌跡”の表示を実装します。今まで説明してきたタッチの仕組みに加え、GL2.Primitive Core.UpdateEmitterというクラスを使います。少し複雑になりますが、1つずつ見ていきましょう。

 LIST5のソースコードを見てみましょう。

LIST5:剣の軌跡を表示する実装
var GL2 = require('../NGCore/Client/GL2').GL2;
var UI = require('../NGCore/Client/UI').UI;
var Device = require('../NGCore/Client/Device').Device;
var Util = require('./lib/Util').Util;
var Trajectory = require('./entity/Trajectory').Trajectory;

//----- 初期化処理
function initialize() {
  //----- 画面の向きを横に
  Device.OrientationEmitter.setInterfaceOrientation(
  Device.OrientationEmitter.Orientation.LandscapeLeft);

  //-----
  var width = UI.Window.getWidth();
  var height = UI.Window.getHeight();

  //-----
  var bg = Util.makePrimitive(0, 0, height, width, [0.0, 0.0, 0.0]);
  GL2.Root.addChild(bg);

  //-----
  var target = new GL2.TouchTarget();
  target.setSize([height, width]);
  GL2.Root.addChild(target);

  //----- 剣の軌跡
  var trajectory = new Trajectory(target.getTouchEmitter());
  bg.addChild( this._trajectory._node );
}

//----- 起動時に呼ばれる
function main() {
  new UI.GLView({
    frame: [0, 0, UI.Window.getWidth(), UI.Window.getHeight()],
    onLoad: initialize
    }).setActive(true);
}

 mainは今までと同じ内容ですね。initializeの中を見ていきましょう。

//----- 画面の向きを横に
Device.OrientationEmitter.setInterfaceOrientation(
Device.OrientationEmitter.Orientation.LandscapeLeft);

 新しいクラスDevice.OrientationEmitterが出てきました。Device.OrientationEmitterは端末の向きの変化を通知したり、端末の向きを設定するためのクラスです。ここではsetInterfaceOrientationメソッドを呼び出し、横向きの描画を行うように設定しています。これで横長の画面でアプリが動くことになります。

//----- 剣の軌跡
var trajectory = new Trajectory(target.getTouchEmitter());
bg.addChild( this._trajectory._node );

 次に出てきた新しいクラスはTrajectoryです。これは「忍者ロワイヤル」で切り出した“剣の軌跡を表示する”ためのクラスです。ここではコンストラクタにTouchEmitterを引き渡しています。

 Trajectoryクラスの中身は、LIST6のようになります。今まで見てきたものより複雑なコードですが、1つずつ見ていきましょう。

LIST6:Trajectory.js
var Core = require('../../NGCore/Client/Core').Core;
var GL2 = require('../../NGCore/Client/GL2').GL2;
var utils = require('../../DnLib/Dn/utils').utils;

exports.Trajectory = Core.MessageListener.subclass({

  initialize: function(touchEmitter, length, colorHead, colorTail) {
    //-----
    this._emitter = touchEmitter;
    this._maxLength = length || 20;
    this._colorHead = colorHead || [1, 1, 0];
    this._colorTail = colorTail || [1, 0, 0];
    this._bladePower = 1.0; // 1.0 が MAX. 刀を折り返すたびに威力が減って軌跡が細くなる
    this._vertexList = [];
    this._isMoving = false;

    //-----
    this._emitter.addListener( this, this.onTouch );

    //-----
    this._node = new GL2.Node();

    //-----
    var p = new GL2.Primitive();
    p.setType( GL2.Primitive.Type.TriangleStrip );
    for (var i=0; i < this._maxLength * 2; i++) {
      var v = new GL2.Primitive.Vertex([0, 0], [0, 0], [1, 0, 0]);
      p.pushVertex(v);
    }
    this._prim = p;
    this._node.addChild( this._prim );
    this._node.setDepth( 1 );

    //-----
    Core.UpdateEmitter.addListener( this, this.onUpdate );
  },

  //--------------------------------------------------------------------------
  destroy: function() {
    Core.UpdateEmitter.removeListener(this);
    this._emitter.removeListener(this);
    this._prim.destroy();
  },

  //-------------------------------------------------------------------------

  getVertexList: function() {
    return this._vertexList;
  },


  getNode: function() {
    return this._node;
  },


  //--------------------------------------------------------------------------
  onUpdate: function( delta ) {

    var v = this._vertexList;
    var p = this._prim;
    if (v.length >= 2) {
      p.setVisible( true );
    
      var bladeWidth = 10 * this._bladePower;
      for (var i=0; i < v.length - 1; i++) {
        var grad = 1 - (i / v.length);
        var r = (this._colorHead[0] * grad) + (this._colorTail[0] * (1-grad));
        var g = (this._colorHead[1] * grad) + (this._colorTail[1] * (1-grad));
        var b = (this._colorHead[2] * grad) + (this._colorTail[2] * (1-grad));
      
        var w = (i < 4) ? (i * 0.22) : g;
        var bw = w * bladeWidth;
      
        var vx = v[i ].x;
        var vpx = v[i+1].x;
        var vy = v[i ].y;
        var vpy = v[i+1].y;
      
        //----- 進行方向と直角に軌跡の太さの方向を算出
        if (! v[i].dx) {
          var theta = Math.atan2( vy - vpy, vx - vpx ) * 180 / Math.PI;
          v[i].dx = utils.cos( theta + 90 );
          v[i].dy = utils.sin( theta + 90 );
        }
      
        p.setVertex( i*2+0, new GL2.Primitive.Vertex(
          [v[i].x - v[i].dx * bw, v[i].y - v[i].dy * bw], [0, 0], [r, g, b]));
        p.setVertex( i*2+1, new GL2.Primitive.Vertex(
          [v[i].x + v[i].dx * bw, v[i].y + v[i].dy * bw], [0, 0], [r, g, b]));
      }
      
      //----- 余った頂点は末尾にまとめる
      var lastVertex = new GL2.Primitive.Vertex(
        [v[ v.length - 1 ].x, v[ v.length - 1 ].y], [0, 0], [0, 0, 0]);
      for (var i = v.length - 1; i < this._maxLength; i++) {
        p.setVertex( i*2+0, lastVertex );
        p.setVertex( i*2+1, lastVertex );
      }
    
    } else {
      p.setVisible(false);
    }
    
    if (v.length > 0 && !this._isMoving) {
      v.pop();
    } else {
      this._isMoving = false;
    }
  },
  //--------------------------------------------------------------------------
  onTouch: function( touch ) {

    var x = touch.getPosition().getX();
    var y = touch.getPosition().getY();
    var vList = this._vertexList;
    
    this._isMoving = false;
    
    switch (touch.getAction()) {

    case touch.Action.Start:
      break;
      
    case touch.Action.End:
      break;
      
    case touch.Action.Move:
      vList.unshift( {x:x, y:y} );
      this._isMoving = true;
      break;

    default:
      break;
    }
    
    if (vList.length > this._maxLength) {
      vList.pop();
    }
    
    return touch.getAction() === touch.Action.Start;
  }
});

次のページ
ngCoreでクラスを定義

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
ゲームエンジン「ngCore」最速チュートリアル連載記事一覧

もっと読む

この記事の著者

沖原 正剛(株式会社ディー・エヌ・エー)(オキハラ マサタカ(カブシキガイシャディー・エヌ・エー))

株式会社ディー・エヌ・エー ソーシャルゲーム統括部 所属。コミュニティーエンジン株式会社でネットワークゲームの開発・運営に携わる。2010年6月株式会社ディー・エヌ・エー入社。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

【AD】本記事の内容は記事掲載開始時点のものです 企画・制作 株式会社翔泳社

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/6324 2012/02/23 19:01

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング