同人ゲームの開発に携わって僕が感じた10の事柄について

インディー・ゲームの開発に携わって僕が感じた10の事柄について
http://ch.nicovideo.jp/ayasawa_s/blomaga/ar747373

このアーティクルはなかなかに当を得ていて良いものだったので、僕たちは類似した記事を書くことにしました。そして、同人ゲーム制作について一般に誤解されていることを明らかにしました。
(以上パクリ)

続きを読む

賢く実装する Unity その2

色々状況も実装も変わったので1は消去しました。
Effective Unity とか欲しい。

Prefab の挙動を正しく知り、正しく使おう

 プレハブとは、ヒエラルキー上にあるオブジェクトをまるで切り取ったかのように状態保存し、使い回せる形にする Unity の要の機能である。使いこなせばとても便利だが、その一方で少々クセがあり、修羅場時にハマる原因となることも確か。正しく知り、正しく使うことがマスターアップ直前に焦らないための第一歩だと思う。

  • オブジェクトを Prefab 化すると、コンポーネントやそのパラメータ、オブジェクト構造や Transform などが保存される。
  • Prefab と結びついているオブジェクトの状態を変更し、Apply を押すと、元の Prefab にその状態が反映され、その Prefab を使った他のオブジェクトにもその状態が同期される。これはシーンもまたぐ。
  • Prefab を public の GameObject 変数などに代入して Instantiate すると、全く同じ複製がヒエラルキー上に生成される。
  • Prefab を中に含んだ Prefab は(現行バージョンでは)できない。

実際にぼくがゲームを作る上で心がけている運用は以下の通りである。

続きを読む

ベヨネッタ2寸感

ベヨネッタ2クリアしました。

全般
  • (+)操作が単純で心地良い 3D アクションが楽しめた
  • (+)モーションが美しく気合を感じた
  • (+)さっくりと遊べてストレスの無い構成
    • (+)プレイを中断しても次回プレイの動機が強く、あっという間にゲームをクリアしてしまった
    • (+)Wonderful101 のように、次どこへ向かえばいいか分からないというケースがなかった
    • (-)悪く言えば一本道。ただ、 3D アクションはこのくらい単純でいいと思う
  • (+)ストーリーが前作よりは分かりやすかった
  • (-)前作よりクライマックス感が減ったように感じた。
  • (-)前作と比べて思わず「真似したくなる演出」というものがなかった。
    • ベヨネッタ自身が闇を抱える前作と比べ、今作は明るいイメージが先行するので、退廃的な雰囲気が消えた?
  • (-)前作と比べてラストへの盛り上がりに欠ける
    • ラスボスの演出も前作よりショボい。スケールも小さい
  • (-)成長要素がオマケ程度?テクニックを買ってもあんまり使わなかった
画面
  • (+)ステージや敵の造形が凝っていて勉強になった
  • (-)相手の攻撃タイミングがまだ少し分かりにくい
特殊ステージ
  • (+)前作より長さが適切に感じた
  • (-)操作方法が複雑すぎる
総評
  • (+)いいゲームです。買いましょう。
  • (-)もう少し早く出ていれば……

MeshFilterにMeshのポリゴン数を表示

やってみたら思いのほか簡単だった。
むしろ最初からポリゴン数表示くらいしてほしいものだが。


using UnityEngine;
using System.Collections;
using UnityEditor;
 
[CustomEditor(typeof(MeshFilter))]
public class PolygonCounter : Editor
{
	public override void OnInspectorGUI()
	{
		base.OnInspectorGUI();

		MeshFilter filter = target as MeshFilter;
		string polygons = "Polygons: " + filter.sharedMesh.triangles.Length/3;
		EditorGUILayout.LabelField( polygons );
	}
}

上のスクリプトを適当な名前.csとつけてEditorフォルダにぶちこみましょう。

オブジェクトをTerrain(地形)に転写

なんか試したら一瞬で出来たので。

コースのようなペラっとしたオブジェクトを選択してメニューを選ぶと、その部分の地形がそのオブジェクトに吸い付くように整地される。勿論、それまでの地形はキープ。

以下をEditor/フォルダにぶち込み。

//--------------------------------------------------------------------------------
// - ObjectToTerrain -
//--------------------------------------------------------------------------------
//
//	オブジェクトをTerrainに転写
//	元のTerrainはキープ
//
//--------------------------------------------------------------------------------

using UnityEngine;
using UnityEditor;
 
//--------------------------------------------------------------------------------
public class Object2Terrain : EditorWindow
{
	//----------------------------------------------------------------------------
	[MenuItem ("Edit/Export/Object to Terrain")]
	static void DoObject2Terrain()
	{
		BeginTransfer();
	}

	//----------------------------------------------------------------------------
	static void BeginTransfer ()
	{	
		if ( !Selection.activeGameObject ) {
			Debug.Log( "[Object2Terrain] No object is selected. " ); return;
		}

		TerrainData terrain_data = Terrain.activeTerrain.terrainData;
		Undo.RecordObject( Terrain.activeTerrain, "Object to Terrain" );
 
		GameObject	 target = Selection.activeGameObject;
		MeshCollider target_col = target.GetComponent<MeshCollider>();
		if ( !target_col ) {
			Debug.Log( "[Object2Terrain] No collider is detected in selection." ); return;
		}
		Bounds targetbounds = target_col.bounds;
		Bounds bounds = Terrain.activeTerrain.collider.bounds;
		
		// Do raycasting samples over the object to see what terrain heights should be
		float		cTerrainY		= Terrain.activeTerrain.GetPosition().y;
		int			cHMWidth		= terrain_data.heightmapWidth;
		int			cHMHeight		= terrain_data.heightmapHeight;
		float		cTerrainHeight	= terrain_data.size.y;

		float[,]	heightmap		= new float[ cHMWidth, cHMHeight ];	
		float[,]	originmap		= terrain_data.GetHeights( 0, 0, cHMWidth, cHMHeight );
		Vector3		ray_origin		= new Vector3( bounds.min.x, targetbounds.max.y * 2, bounds.min.z );
		Ray			ray				= new Ray( ray_origin, Vector3.down );
		RaycastHit	hit				= new RaycastHit();
		float		cRayDistance	= targetbounds.size.y * 4;
		Vector2		cNextDiv		= new Vector2( bounds.size.x / cHMHeight , bounds.size.z / cHMWidth );

		for (int i = 0; i < cHMWidth; i++)
		{
			for (int j = 0; j < cHMHeight; j++)
			{
				bool result = target_col.Raycast( ray, out hit, cRayDistance );
				if ( result ) {
					heightmap[i, j] = ( hit.point.y - cTerrainY ) / cTerrainHeight;
				} else {
					heightmap[i, j] = originmap[i, j];
				}
		   		ray_origin.x += cNextDiv.x;
		   		ray.origin = ray_origin;
			}
			ray_origin.z += cNextDiv.y;
	  		ray_origin.x = bounds.min.x;
	  		ray.origin = ray_origin;
		}

		terrain_data.SetHeights( 0, 0, heightmap );
	}
 
}

メッシュコライダをオブジェクトとTerrainにつけるのをお忘れなく。

役割論理

役割論理とは、ゲーム「ポケットモンスター」における戦術理論の一つである。

ある一定の枠組みの中で、プレイヤーが自由に戦術を編み出し、試行し、対戦を重ねていくと、時にゲームシステムそのものを裏手にとった極端な戦術が生まれることがある。こういった戦術はそのゲームにおける対戦をより奥深いものにするし、そもそもそういった戦術が生まれる器を持ったゲームというのは、即ち対戦ツールとして非常に優れている証である。

よって本記事では「ポケットモンスター」におけるルールと戦術を大雑把に説明しながら役割論理について触れ、どういう経緯でこの極端な戦術が生まれたのか、解説しようと思う。

続きを読む