ボキがかんがえた(わけじゃない)さいつよのシンボルエンカウントMV

「シンボルエンカウント」で検索したら関連キーワードが「嫌い」だったので悩んでいるしんぞですこんばんわ。

なんの前触れもなく敵が襲ってくるよりは、最初から見えていた方が不自然じゃない、という素朴な考えで、シンボルエンカウントっていーなーと思っていたのですが、うざくしないためには幾つかコツがいるようですね。

  • 逃げた敵にもう一度襲われないようにする
  • シンボルの動きに工夫する
  • 作る側がチューニングが楽なようにしておく(=ゲームバランスの調整が楽)

幸い、先達の知恵によって なかなか効率的に作ることができました。

神無月サスケさんの公式プラグインを参考に、
Yanfly先生とトリアコンタン神のプラグイン、およびコモンイベントを使います。

プラグインじゅんび

YEPのコアエンジンとバトルコアエンジンの他に、下記4つのプラグインを入れます。

http://yanfly.moe/2016/04/17/yep-93-force-advantage/
http://yanfly.moe/2016/06/03/yep-105-event-encounter-aid-rpg-maker-mv/
http://yanfly.moe/2015/10/21/yep-20-event-chase-player/
https://triacontane.blogspot.jp/2016/08/blog-post.html

コモンイベントじゅんび

下記のコモンイベントを書きます。

◆ラベル:戦いの直前
◆条件分岐:スクリプト:this.checkPlayerFacingEventBack()
  ◆プラグインコマンド:ForceAdvantage Player
  ◆
:それ以外のとき
  ◆条件分岐:スクリプト:this.checkEventFacingPlayerBack()
    ◆プラグインコマンド:ForceAdvantage Enemy
    ◆
  :それ以外のとき
    ◆プラグインコマンド:ForceAdvantage None
    ◆
  :分岐終了
  ◆
:分岐終了
◆プラグインコマンド:TE_CALL_ORIGIN_EVENT
◆戦闘の処理:{戦うtroopのID}
:勝ったとき
  ◆イベントの一時消去
  ◆
:逃げたとき
  ◆移動ルートの設定:このイベント
  :        :◇不透明度:128
  :        :◇すり抜けON
  :        :◇ウェイト:240フレーム
  :        :◇すり抜けOFF
  :        :◇不透明度:255
  ◆
:負けたとき
  ◆画面のフェードアウト
  ◆プラグインコマンド:MWP_INVALID
  ◆文章:なし, ウィンドウ, 下
  :  :お迎えが来たようですが…
  :  :どうしますか?
  ◆選択肢の表示:今の戦いからやり直す, セーブしたところからやり直す, あきらめる (ウィンドウ, 中, #1, -)
  :今の戦いからやり直すのとき
    ◆画面のフェードイン
    ◆ラベルジャンプ:戦いの直前
    ◆
  :セーブしたところからやり直すのとき
    ◆メニュー画面を開く
    ◆
  :あきらめるのとき
    ◆ゲームオーバー
    ◆
  :分岐終了
  ◆
:分岐終了

イベントじゅんび

敵を配置したいマップにイベントを置き、1ページ目に下記のイベントを書きます。

◆変数の操作:#0027 戦うtroopのID = 26
◆コモンイベント:シンボルエンカウント

グラフィックは「敵っぽいやつ」を用意しましょう。「味方と間違えて話しかけないように、区別のつくように」工夫すると良いでしょう。おれはモノトーンに加工したキャラチップでいくつもりです。

移動ルートはカスタムで下記のようにしてみます。「動作を繰り返す」にチェック。

◆スクリプト:this._chaseRange = 5
◆スクリプト:this._seePlayer = true
◆ランダムで移動

ここまでで、敵1匹ができました。次は、これを複製してみましょう。

イベント複製用のイベントを作成

意味わからん? うん、すまん。説明が下手なのは持病。
ここまで用意したイベントは1個だけなのだけど、トリアコンタンさんのプラグインで簡単に複製することができるので、複製を実行する専用のイベントを1モンスターあたり1こ作ります。

グラフィックなし、自動実行で下記のようにします。変数は自分の環境では28番が空いていましたが、そこはお好みでどうぞ。プログラミングでいう「for」の処理に相当することを行って、プラグインコマンドを任意の回数実行します。

◆変数の操作:#0028 temp:forで回すi = 0
◆ループ
  ◆変数の操作:#0028 temp:forで回すi += 1
  ◆条件分岐:temp:forで回すi ≥ 10
    ◆ループの中断
    ◆
  :分岐終了
  ◆プラグインコマンド:ERS_MAKE_RANDOM 12 passonly
  ◆
:以上繰り返し
◆イベントの一時消去

キャラ名、変えてますか?

RPGに「キャラ名を入力してください」というシーンは必要か否か、自分の感覚だけでは不安だったのでtwitterでアンケートを取ってみました。
姑息にも拡散希望的なテキストをいれた結果、なんと560票もいただいてしまいました! 自分自身のフォロワーさんの数の5倍くらいあります。ありがとうございます!


「そんなん場合によるとしか言えんがな」という方と、「変えないよ」という方がほとんどでした。
コメントをいただいたので、要約させていただきます。「その時次第」のさじ加減の参考になるかと思います。

  • べつにめんどいからではなく、世界観やキャラ設定を尊重したいので変えない
  • 主人公の名前を変えると感情移入しやすいので嬉しい
  • DQ3のようにキャラ設定がない仲間の場合は自分で決めたい
  • すでにキャラ設定があって独り立ちしているキャラの場合は変えない
  • MOTHERシリーズのように、命名することを含めて完成された演出がある場合は自分で決めたい
  • 名前をつけることで「自分のものにした」感覚があってプレイに身が入る、という側面もある

一般的なJRPGというか「ちかごろのRPG」の場合は、すでに仲間のキャラが立っている場合が多いので、仲間の名前をつける機能は使わない人が多そうですね。
主人公の名前は「プレイヤーは自分の分身」という堀井雄二先生スタイルを維持している場合は、やはり決められるようにしたほうがいいでしょう。ただ、その場合でも「デフォルト名」は用意したほうがいいかもしれません。
参考までに、ゼルダシリーズではリンクほど有名なキャラでも「主人公はしゃべらない感情移入の対象」スタイルなせいか、プレイ開始時に名前が変えられるようになっています。以前すれ違い通信で確認したところ、3割ほどの人が自分のあだ名らしき名前に変えていました。(逆に言うと7割は変えていないということですが…)

と、いうわけで、今作っている自分のゲームでは「主人公の名前はデフォルト名もあるけど変えられる、仲間はこちらが設定した名前のまま」という仕様でいこうと思います。
ありがとうございました!

「グレー表示」をもっと薄くするプラグイン

ツクールMVを黒背景から白背景に変えると、「薄くなっている文字」があんまり薄く見えなくて、見分けがつかなくなってしまいました。
これを簡単なフックで変えられたのでプラグインとして置いておきます。
SNZ_DisabledItemOpacity
調べれば誰でも書けるレベルのものなので、特に権利は主張しないです;ご自由にどうぞ。

文字色を黒にしたいときのコツ

ツクールのデフォルトは黒っぽい背景に白文字ですが、白っぽい背景に黒文字というのも、なかなか読みやすくていいものです。
k本的にはimg/system/window.pngを編集すればよし。ただし、デフォルトでは袋文字の縁取りが黒なので、読みづらいどころか成立しないものになってしまいます。縁取りの色を変えるか、縁取り自体を消す必要があります。
いくつかのプラグインでこれが実現します。

メニュー画面

準公式プラグインTextDecoration(「ニナと鍵守の勇者」内)
スクリーンショット 2016-08-15 20.44.25

メッセージ画面

みんな大好きYEP_MessageCore
スクリーンショット 2016-08-15 20.44.09

バトル画面の、いわゆる「バトルログ」(上に表示されるテキスト)

SRD_BattleLogUpgrade
http://sumrndm.site/battle-log-upgrade/
スクリーンショット 2016-08-15 21.03.18
「白い帯に黒い文字」だとかえって不自然になるので、文字色をここだけ変えてみました

メニュー画面をカスタマイズするときに使えそうなプラグイン

回想シーンはドット絵ふう、通常シーンはそうではなく、というのをやりたかったので、ウインドウスキンには触りようがなかったのですが、このたびやりたいことが実現できるプラグイン一式を見つけたのでメモ。いま実家なのでテストとかはまだです。ほぼ自分用メモ。

ウインドウスキンを作中で切り替えられるプラグイン

Windowskin Change

メッセージウインドウのデザインだけ好きな画像で置き換えるときに便利なプラグイン
「通常ウインドウを透明に」「オートピクチャウインドウ
http://garasuzaikunomugen.web.fc2.com/game3.html

バトル画面とマップ画面の両方またはどっちかで、顔グラとHPなどのゲージをパーティ全員分表示
https://github.com/biud436/MV/tree/4a83017ec0e460f089c43eaa407b82470d594b0f/HUD

作ったばかりのGoogle Map API利用サイトなのに「このサイトは地図の 1 日の使用量を超えました。」と言われたら

Basic認証を外すか、APIキーの「この HTTP リファラー(ウェブサイト)からのリクエストを受け入れる」設定を外してみよう!

テスト作業中に認証をかけたディレクトリにファイルを置くのはよくあることですが、Google Maps APIの規約として「一般公開されているサイトに置く以外は有料だよコラ」というのがあるので、ブロックされてしまうようです。

現状(2016年8月)では、APIのリファラ制限をはずすとテストが可能な様なので、本番アップするまではこの項目を空にしておきましょう。よく見ると該当の項目に薄く「このキーを本稼働環境で使用する前に、必ずリファラーを指定してください。」と書いてあるので、本稼動前は空欄でいいよ、ということなのかもしれません。

ブラウザ用のツクールゲーを作成する試行錯誤

帰ってきました爺メン制作委員会。
パソコンデスクを買い換えたので、快適に作業しちょります。

ウェブブラウザ用に書き出した編集中のゲームを、試しにスマホで遊んでみたのですが、まあ落ちる落ちる。

メモリを食う原因は主に

  • グラフィック
  • 音声

だと思うので、これらを重点的に調整します。

グラフィック

まず、以前紹介した「ブラウン管風にエフェクトをかけるプラグイン」は残念ながらオフ。画面にズームを掛けっぱなしにしているというだけでも、かなりクラッシュが増えるようです。

他に、セーブ画面の改造プラグインを使っていたのですが、これの便利機能「セーブした場所のスクリーンショットを添える」を不使用に。セーブ中にクラッシュするという最悪の事態が激減しました。

あと、戦闘中の魔法エフェクトを試しに切ってみたら、バトルが劇的に速くなりました。スマホゲーの「魔法の演出」って、一枚の画像を拡大縮小させるだけのことが多くて なんかまぬけだな、と思ってしまっていたのですが、キャッシュを節約するためにはしょうがないんですね。

音声

BGMは圧縮しても、尺によっては1メガとか平気でイッてしまいます。今までの感覚で、イベント演出のために音楽をガンガン切り替えると、オープニングが終わるまでの間に落ちてしまったりするし、だいいち通信費が心配です。ここは演出の推敲も含めて、工夫のしどころというとこでしょうか。

フリー素材の音楽は2分くらいあるものが結構あるので、作者さんが許諾されていれば、一部をカットして短いループに編集した方が良いかもしれません。

気になる音声の圧縮率ですが、「Foobar2000」の場合、設定画面のスライダを一番左にしてがっつり圧縮しても、意外と音質が変わったとは感じないものです。ここは主観かもしれませんが…

小ネタ:レスポンシブのページで改行位置を調整しよう!

ちょっと長めの見出しをレスポンシブサイトで表示すると、
PCでは

こんな感じで一行にすっきり表示されている見出し


スマホなどの幅の狭いデバイスになると

こんな感じで一行にすっきり表
示されている見出し


変なところで強制的に折り返されてしまうことがあります

今までは、対策として<br>タグを書いて幅の広いデバイスではdisplay:noneで隠していたのですが、もっとスマートな方法を思いつきました

<span class=”nobraking”>こんな感じで一行に</span><span class=”nobraking”>すっきり表示されている見出し</span>

と、「ここからここまでは途中改行されるとおかしい」と思う文節をspanでくくっておいて、それぞれにスタイルシートでdisplay:inline-blockを指定しておくのです

こんな感じで一行に
すっきり表示されている見出し

てな感じで回り込んでくれるはずです

Googleが推奨する「戻るボタン対応・SEO」の無限スクロールを使ってみた

無限スクロール、いいですよね。
ところが巷に配布されているプラグインの多くは、ブラウザのバックボタンで戻ったら最初から読み込み直しでヴァー、ということがあってユーザビリティ上よろしくないのです。

Google先生が対応策としてサンプルのJavascriptをつくってくれたので、いろんな案件で使えるようにちょっと手を入れてみました。

元にしたのはこちらのサンプルです。
http://scrollsample.appspot.com/items

解説は下記ブログ記事をご覧ください。
Googleが推奨するSEOに適した無限スクロールの構成方法

  • あらかじめページを分割しておく。いわゆるページネーション
  • 分割されたページのうち1つを表示(1ページ目でなくても見られるようにする)
  • その上下に前のページ、次のページをAjaxで読み込んでいく
  • 今表示している部分が何ページ目に相当するかに合わせて、URLを動的に書き換える

自分なりに書き換えてみたソースは下記です。jQueryを読み込んだ後に実行してください。


$(function (){
//infinite scroll
var prev_data_selector = '.prev.page-numbers'; //「前へ」リンクのaタグのセレクタ
var next_data_selector = '.next.page-numbers'; //「次へ」リンクのaタグのセレクタ
var itemWrapperSelector = '.list_infinite'; //無限ローディングのwrapperとなるタグのセレクタ
var itemWrapper = $(itemWrapperSelector);
var itemInnerSelector = '.list_infinite_item'; //無限ローディングの本体(本文)となるタグのセレクタ
var itemPagerSelector = '.pagination'; //ページャーのセレクタ
var itemPager = $(itemPagerSelector);
var prev_data_url;
var next_data_url;
var next_data_cache;
var prev_data_cache;
var last_scroll = 0;
var is_loading = 0;

if(itemWrapper[0]) {
	prev_data_url = $(prev_data_selector).attr('href');
	next_data_url = $(next_data_selector).attr('href');
	fadeInItem(true);
	initPaginator();
	loadPrevious();
    // if we have enough room, load the next batch
    if ($(window).height()>itemWrapper.height()) {
      if (next_data_url!== '') {
        loadFollowing();
      } 
    }
}

function initPaginator() {
	$(document).off( 'scroll');
  $(document).on( 'scroll', function(){
    // handle scroll events to update content
    var scroll_pos = $(window).scrollTop();
    if (scroll_pos >= 0.9*($(document).height() - $(window).height())) {
      if (is_loading===0) {
		  loadFollowing();
	  }
    }
    if (scroll_pos <= 0) {
      if (is_loading===0) {
		  loadPrevious();
	  }
    }
    // Adjust the URL based on the top item shown
    // for reasonable amounts of items
    if (Math.abs(scroll_pos - last_scroll)>$(window).height()*0.1) {
      last_scroll = scroll_pos;
      $(itemInnerSelector).each(function() {
        if (mostlyVisible(this)) {
          history.replaceState(null, null, $(this).attr('data-url'));
          return(false);
        }
      });
    }
  });
}


function loadFollowing() {
  if (next_data_url){
    is_loading = 1; // note: this will break when the server doesn't respond
    itemPager.hide();
    if (next_data_cache) {
      showFollowing(next_data_cache);
      is_loading = 0;
    } else {
		$.ajax({
			url: next_data_url,
			type: 'get',
			dataType: 'html',
		}).success(function(data){
	        showFollowing(data);
   		     is_loading = 0;
		});
    }
  }
}
    function showFollowing(data) {
	var out_html = $($.parseHTML(data));
      itemWrapper.append(out_html.find(itemWrapperSelector).filter(itemWrapperSelector)[0].innerHTML);
	  initPaginator();
      next_data_url = out_html.find(next_data_selector).filter(next_data_selector).attr('href');
      next_data_cache = false;
		$.ajax({
			url: next_data_url,
			type: 'get',
			dataType: 'html',
		}).success(function(preview_data){
	        next_data_cache = preview_data;
		});
    }

function loadPrevious() {
  if (prev_data_url){
    is_loading = 1; // note: this will break when the server doesn't respond
    itemPager.hide();
    if (prev_data_cache) {
      showPrevious(prev_data_cache);
      is_loading = 0;
    } else {
		$.ajax({
			url: prev_data_url,
			type: 'get',
			dataType: 'html',
		}).success(function(data){
	        showPrevious(data);
   		     is_loading = 0;
		});
    }
  }
}
    function showPrevious(data) {
	var out_html = $($.parseHTML(data));
      itemWrapper.prepend(out_html.find(itemWrapperSelector).filter(itemWrapperSelector)[0].innerHTML);
	  initPaginator();
      var item_height = $(itemInnerSelector + ':first').height();
      window.scrollTo(0, $(window).scrollTop()+item_height); // adjust scroll
	  prev_data_url = out_html.find(prev_data_selector).filter(prev_data_selector).attr('href');
      prev_data_cache = false;
		$.ajax({
			url: prev_data_url,
			type: 'get',
			dataType: 'html',
		}).success(function(preview_data){
	        prev_data_cache = preview_data;
		});
    }

function mostlyVisible(element) {
  var scroll_pos = $(window).scrollTop();
  var window_height = $(window).height();
  var el_top = $(element).offset().top;
  var el_height = $(element).height();
  var el_bottom = el_top + el_height;
  return ((el_bottom - el_height*0.25 > scroll_pos) && 
          (el_top < (scroll_pos+0.5*window_height)));
}


});

グループを繰り返せるカスタムフィールド用プラグインを探してみた

みんな使っているのになぜか標準仕様がおざなりで使えない「Wordpressのカスタムフィールド」、プラグインをいろいろためしているのですが、グループ化したカスタムフィールドを繰り返す(「画像とキャプションの組み合わせ」を任意の個数追加、ってかんじ)のを実現できるもの、となるとかなり候補が絞られます。CMSとして使う際には頻出テクニックなんだけどね。

Custom Field Suite

http://docs.customfieldsuite.com/

おそらく現状最良の選択。ただ、細かいけど困るバグが多くてバグ対応も遅いのがなあ。「必須」にチェックを入れているのに保存したらチェックが消えている、とか。「次のアプデで対応します」て言ったきり1ヶ月放置されてたり。

グループ化した時だけテンプレートでの出力時に独自の関数を使うが、他はpost_customのままでいけそうなのも好感触。最初にグループ化するための操作が少しだけわかりづらいが、わかってしまえばOKだし、更新する人には関係ないことなので大丈夫。

※備考:「チェックボックスのリスト」は作れません。カスタマイズして追加する方法もあるようですが、ここはWordpress本来の仕様である 「カスタムフィールドは名前:値の1対1の組み合わせ」というのを尊重して、各チェックボックスの項目ごとに「真/偽(簡易チェックボックス)」を増やし て対応してみます

ACF

https://www.advancedcustomfields.com/

超有名プラグインだけど、繰り返しフィールド機能は有料。しかも内部的に独自のデータ処理をやっているみたいで、他のプラグインへの乗り換えが難しかったりWordpress標準の関数が一部使えなかったりと悪い評判も聞いたりする。

Smart Custom Fields

http://2inc.org/blog/category/products/wordpress_plugins/smart-custom-fields/

必要最小限の機能を、できるだけWordpressの仕様に則って実現するというコンセプトなので後々乗り換えることになっても安心そう。
ただし、現状では「必須項目」を設定できないので、仕事としての制作にはまだ使えないと思う。

カスタムフィールドテンプレート

http://ja.wpcft.com/

おれの環境では本文が文字化けして使えなかった。同様の報告例あり。
それと、エディタ画面でのボタン配置や操作感が悪く、仕事として作る際にはお客さんに説明しづらい。
プラグインは無料だがマニュアルが有料。