プロが教える、ゲーム企画書の作り方【就活/会社】

 f:id:eiki_okuma:20190315090101j:plain

プロが教える、ゲーム企画書の作り方

 という有料記事を note にアップロードしました*1。500円で就職や会社での活動にダイレクトに響く実践的なノウハウが入手できます。

 何故有料にしたかというと、普段ブログに書いてる「読み物」と違って実践的な内容しか書いてないので、頭の中であれこれ考えながら真面目に読まないと意味がないからです。業務や制作の中で学んだ多くの経験・知識を詰め込んでいるので、流し読みしてほしくないなあ、というのも一つです。

 まあ、実験的な試みですが、後悔させない内容にはなっているので良ければ読んでみてください。途中まで試し読み可能です。

 

note.mu

 

 

*1:はてなブログにも有料記事機能がつけばいいのに……

秒でオーディオを実装する

とりあえず一日~二日で作ったプロトタイプに音を付けたい時に使うスクリプト

//--------------------------------------------------------------------------------
//	- AudioManager -
//--------------------------------------------------------------------------------
//
//	オーディオ全般
//
//--------------------------------------------------------------------------------

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public enum ESeID
{
	None,

	Decide,
	Cancel,
	Select,
	Bomb,
	Dash,
	
	Max
}

//--------------------------------------------------------------------------------
public class AudioManager : MonoBehaviour
{
	AudioSource	mBGM = null;
	AudioSource [] mSEList = new AudioSource[cSEMax];

	const int cSEMax = (int)ESeID.Max;

	//----------------------------------------------------------------------------
	void Awake ()
	{
		DontDestroyOnLoad( gameObject );
		var t = transform;

		mBGM = GetComponentSafe<AudioSource>( t, "BGM" );
		for( ESeID e = ESeID.None+1; e < ESeID.Max; e++ )
		{
			mSEList[(int)e] = GetComponentSafe<AudioSource>( t, e.ToString() );
		}
	}
	
	//----------------------------------------------------------------------------
	public void playSE ( ESeID id )
	{
		if ( mSEList[(int)id] != null ) mSEList[(int)id].Play();
	}
	
	//----------------------------------------------------------------------------
	public void startBGM()
	{
		if ( !mBGM.isPlaying ) mBGM.Play();
	}

	//----------------------------------------------------------------------------
	// コンポーネントの取得
	private Type GetComponentSafe<Type>( Transform parent, string gameobject_name )
	where Type : Component
	{
		Transform temp = parent.Find( gameobject_name );
		if ( !temp ) return null;
		return temp.GetComponent< Type >();
	}
}

使い方:

f:id:eiki_okuma:20190321000711j:plain

  1.  "Audio"と名前の付けた空のゲームオブジェクト*1に、"BGM"と名前の付けたゲームオブジェクトと、 ESeID の中で指定した SE の名前のゲームオブジェクトをぶら下げる("Decide"とか"Dash"とか)。BGM と SE にはそれぞれ AudioSource を追加しておく。
  2. "Audio" にこの AudioManager を追加する。
  3. 後は適当なところから playSE や startBGM を呼ぶ。

*1:この名前は別になんでもいい

物事のパッケージング - 自己満足が争いを生む本当の理由

f:id:eiki_okuma:20181101211103j:plain

 こんな記事が先週話題になっていた。

 流し読みすると一見よくある「妻の言うことを聞いてばかりでも不満が溜まっていくだけだった!」みたいな内容なのだが、実は本質はまったく別の所にある。その正体は、現代社会において度々発生する物事のパッケージング問題だ。たとえば、以下のようなケースをよく耳にしないだろうか。

  • 自分は率先して家事をやったのに、妻の小言が増える一方だった。(今回のケース)
  • 積極的に育児に関わってきたはずなのに、妻からはそう思われていない。それどころか、子供も妻に懐いている。
  • 先陣を切って精力的に仕事をやったのに、上司や同僚からの評価が低い。あいつらはちゃんと俺を見ていない。
  • 閑散期に深夜まで残っているプログラマーがいる一方で、定時に帰宅しているプログラマーもいる。しかも、前者の方が優秀プログラマーなのに。
  • 彼はよく家事を手伝ってくれるけど、どうも思いやりが足りない。(逆のケース)
  • あのディレクター、対外的に偉そうなこと言ってるけど、現場では迷惑な言動ばかりでプロジェクトがめちゃくちゃだ。(逆のケース)

 これらは全て、当事者間の物事のパッケージングが上手く擦り合わさってないことに起因する。あらゆる現代人を悩ませるこの物事のパッケージング問題とは何か、詳しく説明していく。

続きを読む

【Unity】iOS / Android でレビューを促す。(2018年10月版)

 f:id:eiki_okuma:20181022194328p:plain

 ちょっと目を離してる隙にどんどん仕様が変わっていくよ!!

iOS

if ( !UnityEngine.iOS.Device.RequestStoreReview() )
{
	yield return Ask();        // レビューするかどうか聞く
	if ( answer == 0 ) { 
		string url = "itms-apps://itunes.apple.com/jp/app/idXXXXXXXX?mt=8&action=write-review";
		Application.OpenURL(url);
	}
}

 「XXXXXXXX」はアプリごとに変える部分。(AppStoreのURLより)
 iOSは10.3から専用のネイティブダイアログが強制されるようになったが、全ての端末が10.3以上であるとは限らないので、 RequestStoreReview() で分岐する必要がある。このネイティブダイアログは「レビューしてください!→☆☆☆☆☆ OK/キャンセル」というものなので、これが使える場合はレビューするかどうか聞く必要はない。

 false が返ってくる場合は未対応端末なので、アプリ側でレビューするかどうかを聞く。

RequestStoreReview()の諸注意
  • 呼び出した後、出てくるかどうかは OS 側で調整する。例えば、前回「いいえ」を押した場合はしばらく出てこなくなる。この設定はユーザ側も変えられる。
  • 故に会話中にダイアログを出したい場合、文言に気をつけないと不自然になる。
  • 戻り値は「レビューしたかどうか」には関係ない。
  • Apple 側の想定としては、ゲームの合間に適当に何回も呼んでね! という感じだと思われる。

 

Android

yield return Ask();        // レビューするかどうか聞く
if ( answer == 0 ) { 
	Application.OpenURL( "market://details?id=XXXXXXXX" );
}

 Android はシンプルに URL を開くだけ。「XXXXXXXX」はアプリごとに変える部分。(パッケージのID - com.illucalab.ham など)

 

まとめると

#if UNITY_IOS
if ( !UnityEngine.iOS.Device.RequestStoreReview() )
#endif
{
	yield return Ask();        // レビューするかどうか聞く
	if ( answer == 0 ) {
#if UNITY_IOS
		string url = "itms-apps://itunes.apple.com/jp/app/idXXXXXXXX?mt=8&action=write-review";
#else
		string url = "market://details?id=XXXXXXXX";
#endif
		Application.OpenURL(url);
	}
}

 

文言について

  • 前述の通り、iOS は前後の文言に気をつけること。というか会話や説明の合間に出すことを推奨されていない。多分。
  • iOS は「☆5にして!」という文言はガイドライン違反。Android はOK。
  • 両 OS 共「レートしたかどうか」を取得することはできない。
  • 英語では「Rate this game!」という表現になるので、UI で英語を使いたい場合は注意。「Review」は「審査」に近い意味合いになる。

 

スパイダーマン(PS4)寸感

 ビルの合間を猛スピードで移動するスパイダーマンの姿を映画館で見て、魅力を感じなかった少年はいないだろう。テレビゲームの飛躍的な進歩はハリウッド渾身の絵作りに少しずつ近づいてはいたが、それは PS4 版でとうとう臨界点を超えた

  手放しで褒めてもいい出来なのだが、それはあまりにも多くの人がやっているので、一つ一つ要素を紐解きながら最新 AAA ゲームのベンチマークとして少し紐解いてみたい。

 まあ、結論から言うと「買え」ってことなのだが。

 

続きを読む

Google play game services の Saved game を Unity から使う。

f:id:eiki_okuma:20180929105308p:plain

資料が少なすぎる。

ので書きます。

マトモな解説資料ここくらい?↓
https://answers.unity.com/questions/894995/how-to-saveload-with-google-play-services.html

 

Saved games って何?

 Android のグーグルプレイには AppStore における Game Center のように Google play game services (GPGs) というものがあり、そのうち一つの機能に Saved game というモノがある。いわゆるクラウドセーブで、どの端末でも同じ所から始められるようにする、というコンセプトらしい。

 アイテム課金を扱うならクラウドセーブは必須だし、あるなら利用しましょうってことで使ってみることにした……が、資料(特に日本語記事)が少ない! ので今回の実装をまとめておく。

 

準備

 準備の各項目はググれば出てくるので詳細説明なし。

  1. GPGs を Unity に組み込む。
  2. GooglePlayConsole からゲームサービスを選び、アプリを紐付け。保存済みゲームを ONにする。
  3. Android でアプリを起動し、GPGs が正常に読み込まれていることを確認する。(内部テストなどで Google Play から直接ダウンロードしないと認証されないっぽい)
続きを読む

【Unity】シーンカメラの位置をセーブ&ロード

意外と知られていなさそうな、シーンビューのカメラの位置をセーブ&ロードする方法。製作時の見た目の調整ならゲームカメラを使えばよいが、製作過程の記録などに役立つ……かも。

ポイントは lastActiveSceneView の camera ではなく pivot を使う所。camera を使うと、ロード時に上手くいかない。

 

using UnityEditor;

static Vector3 mCameraPos = Vector3.zero;
static Quaternion mCameraRot = Quaternion.identity;

[MenuItem("配置ツール/カメラ位置をセーブ",false , 10)]
static void SaveCameraPos()
{
	mCameraPos = SceneView.lastActiveSceneView.pivot;
	mCameraRot = SceneView.lastActiveSceneView.rotation;
}

[MenuItem("配置ツール/カメラ位置をロード",false , 11)]
static void LoadCameraPos()
{
	SceneView.lastActiveSceneView.pivot		= mCameraPos;
	SceneView.lastActiveSceneView.rotation	= mCameraRot;
	SceneView.lastActiveSceneView.Repaint();
}