一覧表示(index)

一覧表示(Index)

index アクションを作る

いじるファイルはControllerフォルダに作成されたファイル。
このファイルに各アクションを追加していく。

def index
    @projects = Project.all
end

@~は変数みたいなもん。all関数は全てのモデルを引っ張ってくるものらしい。

Viewを作る

ファイル名:アクション名(index).html.erbにする。これも規約

<h1>Projects</h1>
<ul>
<% @projects.each do |project| %>
   <li><%=project.title %></li>
<% end %>
</ul>
  • rubyの制御構造は<%...%>~~<% end %>に書いていく
  • 式の評価と表示は<%= ...%>に書く

基本的な流れとして、コントローラーアクションを作って、それに対応するViewを作る。

Rails はじめた

重要な考え方

  • MVC(model:view:controller)
  • DRY (Don't Repeat Yourself)
  • 設定より規約

基本的なコマンド

Railsアプリケーション作成
rails new appname

このコマンドでアプリケーションに必要なファイルが作成される。

サーバの起動
rails server(sだけでもOK)

http://localhost:3000でアクセス

今使ってるDBにアクセス
rails db

sqliteが起動するので以下のコマンドを入力

sqlite> .schema

//戻る場合は
sqlite> .exit
コンソール
rails console

作ったモデルなどをインタラクティブにいじれる。

>p = Project.new(title:"p1")
>p.save
>p

Project:モデル名、title:DBのテーブル名
この3つで作成、保存、表示

>p = Project.create(title:"p2")
>p.all

createは作成+保存
p.allは全てのモデルを表示する。

このようにインタラクティブにモデルの状態を確認することができる。

作成手順

まずはModelを作る
rails g model Project title
  • gはgenerateの略。
  • Model名は単数、最初は大文字
  • titleは属性(:stringで型を指定するが、デフォルトではstringになる)

migrationファイルが生成されるので、migrateする(テーブルを作成する)

rake db:migrate
controller
rails g controller Projects

Modelでは単数だったのに対して、Controllerは複数系を使うのがrailsの規則。
ControllerとViewに関するファイルが作られる。

ルーティング

Config/routes.rbファイルを編集する。

resouces :projects

URL的なのを自動生成してくれる。

rake routes

ルーティングを表示する。
f:id:nm777taka:20140311012550p:plain
ブラウザがURIにアクセスして、右側のControllerアクションを実行していく。

とりあえず今日はここまで。
次回から各アクションを作っていく。

Unity-6日目 フラッピーバードを作ってみる

今話題のフラッピーバードを作ってみたー。Unityが神すぎて意外に簡単に作れたので手順を残しておく。

スクリプト一覧

  • PlayerControl : 鳥をクリックでジャンプさせる
  • MoveScript : 障害物を移動させる
  • WallManager : 障害物を発生させる
  • CubeScript : あたり判定
  • JudgementScript : クリア判定
  • ScoreManagerScript : スコア変数の管理
  • ScoreScript : スコアの加算と標示
  • Title,EndScnecScript : GUIボタンと画面遷移

画面構成

f:id:nm777taka:20140220023214p:plain

  • Cube(鳥のつもり)
  • EmptyObjectが3つ(スコア表示、スコア管理、障害物発生)

プレイヤーの動作

ジャンプさせる
void Update () 
{
	if(Input.GetMouseButtonDown(0))
	{
		float y_speed = Mathf.Sqrt(2.0f * Mathf.Abs(Physics.gravity.y) * this.jumpHight);
		this.rigidbody2D.velocity = Vector2.up * y_speed;
	}
}
  • 左クリックされたら

Publicでジャンプの高さを設定する変数を用意し、y方向の速度を設定した高さから算出。
速度とY方向ベクトルとの積をrigidbodyの速度に設定。

rigidbody2Dのパラメータ設定

デフォルトの設定だと、ふわふわしてしまったので、適当にパラメータをいじった。
f:id:nm777taka:20140220183330p:plain
重力と質量を増やして、それっぽい動作になった。

障害物を発生させる

ゲームの仕組みは、鳥はその場でジャンプするだけで、障害物が右側から移動してくるというもの。その障害物をCubeの組み合わせで作り、一定間隔で発生させる。

public float appearRate = 5.0f;
private float coolDown;
public GameObject WallPrefab;

appearRateで発生間隔を指定し、coolDown変数で制御する。WallPrefabには作った障害物をインスペクタから割り当てておく。

void Update () 
{

	if(coolDown > 0 )
	{
		this.coolDown -= Time.deltaTime;
	}

	if(coolDown <= 0)
	{
		coolDown = appearRate;

		GameObject wall = Instantiate(this.WallPrefab) as GameObject;

		float y_pos = Random.Range(-5.0f,-0.7f);

		Vector3 new_pos = new Vector3(transform.position.x,y_pos,0);

		wall.transform.position = new_pos;



	}

}

クールダウンが必要な場合は、前フレームからの経過時間を引いていく。Unityにおいて、時間経過を扱う場合はTime.deltaTimeを使うのがいいらしい。クールダウンが終わったら、障害物をインスタンス化する。

  • 障害物を上下にランダムに動かしたい

カメラを見ながら適当に決めた動き幅で、乱数を発生させて、Y軸方向のオフセットとした。新しいベクトルを作ってあげて、障害物のトランスフォームに設定。

障害物を左方向に動かす

前回も書いたけど、復習のためにもう一度。

public Vector2 speed = new Vector2(1,1);
public Vector2 direction = new Vector2(1,0);
private Vector2 movement;

基本的に方向やスピードはインスペクタでいじって調整したほうが楽なので、public宣言

void Update ()
{
	movement = new Vector2 (
		this.speed.x * this.direction.x,
		this.speed.y * this.direction.y);
}

void FixedUpdate()
{
	rigidbody2D.velocity = movement;
}
<||
そして新しいベクトルをUpdate関数内で作ってあげて、rigidbodyの速度に設定。定番の流れかな。

*障害物にぶつかった時の処理
障害物にはboxCollider2Dを設定しておく。今回は床にもあたり判定をつけているため、通常の設定では床とオブジェクトが干渉してうまく動かない。そのため、isTriggerをオンに。そうすることで、あたり判定は行うけど、物理的な干渉をオフにできる。
>|c|
void OnTriggerEnter2D(Collider2D col)
{
	if(col.gameObject.tag == "Player")
	{
		//game end title
		Application.LoadLevel("GameOver");
	}
}

isTriggerに設定した場合のあたり判定の受け取りはOnTriggerEnter2Dで行う。あらかじめプレイヤーにtagをつけて、あたり判定処理を限定にしている。もし、ぶつかったのがプレイヤーの場合はゲーム終了画面に遷移する。

画面遷移

今回初めて画面遷移処理を作った(笑)

  • シーンをBuildSettingから登録(忘れがち)
  • 遷移したいシーンをApplication.LoadLevelに渡してあげる。今回はStringで渡しているけど、int型の数字でもできる。(buldSettingに登録してあるシーンは上から順番に番号がふられる)

クリア判定

f:id:nm777taka:20140220185222p:plain
障害物の間を通ったかどうかを判定するために、新たにクリア判定用のEmptyObjectを作って、親子に設定してみた。(これ以外が一番簡単な気がした...)
このクリア判定オブジェクトに対して、あたり判定処理を行い、スコアを加算していくというような感じ。

スコア処理

スコアを管理するスクリプト

今回、スコアをゲーム終了画面にリザルトとして表示させたかった。しかし、シーンが切り替わるとHierarchyにあるオブジェクトは破棄されてしまうので、スコアも保持できない。
そのため、スコアを管理するスクリプトを作成する。

public static int score;

void Start () {
		DontDestroyOnLoad (this);
	}

そして、DontDestoryOnLoadにスコア管理のオブジェクトを渡しておけば、シーンが変わっても破棄されない!!!スコア管理は基本的にこんな感じでやってくみたい。管理する変数をstatic宣言するのを忘れずに!!

スコアを加算、表示する

スコア管理スクリプトのscore変数を参照して、加算していくだけ。

void AddScore(int score)
{
	ScoreManagerScript.score += score;
}
<||
基本的にスコアはクリア判定時にこの関数を呼び出して行う。あくまで操作を提供するだけ。

>|c|
guiText.text = score.ToString ();

表示はUpdate関数にこの一行を書くだけでOK。scoreはint型なのでstringに変換するのを忘れずに。

画面遷移のボタン

テキストやテクスチャ、ボタンなどのインターフェース要素はOnGUI()の中に処理を記述する。

void OnGUI()
{
	if(GUI.Button (new Rect (Screen.width /2.0f - 20.0f, Screen.height/2.0f, 100, 50),"START"))
	{
		Application.LoadLevel("Main");
	}
}

Screen.でスクリーンのサイズにアクセスできる。
ボタンが押されたら、Mainスクリーンにタイトル画面から遷移する処理は上の感じ.

完成!

f:id:nm777taka:20140220191110p:plain
これにいい感じの素材つけたら、フラッピーの完成だあああ!!

Unity-5日目 2Dスクロールゲーム

よくある弾幕ゲームみたいなの

f:id:nm777taka:20140218015818p:plain

ポイントは背景が無限に続いていくパララックススクロール?
スプライトの基本的な扱いは前回と一緒だけど、パララックスやカメラ追従が加わってる感じ。
とりあえず、チュートリアル見ながらやってみた(英語だったから正確かどうか怪しいけど)
制作の過程で得た気づきとか学んだ事を書いていこうと思う。

スクリプトの説明

  • PlayerScript→操作、攻撃
  • MoveScript→オブジェクトが自動的に移動する処理
  • HealthScript→体力制御
  • ShotScript→銃弾の制御(ダメージ量、オブジェクト破壊)
  • WeaponScript→銃弾発射の制御(クールダウンなど)
  • EnemyScript→攻撃

一枚の絵から複数のスプライトを生成する

f:id:nm777taka:20140218020851p:plain
例えばこのように一枚の絵を複数のパーツに分けたい時は、SpriteEditorを使う。
インスペクターでSprite ModeをMultipleに設定し、SpriteEditorを起動。
欲しい部分を囲む→Applyとするだけで、別々のSpriteに書き出せる!!便利!!

Playerの操作スクリプト

基本的にSpriteを動かすときは、rigidbody2D追加して、addForceかridigdobyのvelocityに速度を与えるかのどっちか。今回はvelocityに値を与える。

void Update () {
		float inputX = Input.GetAxis ("Horizontal");
		float inputY = Input.GetAxis ("Vertical"); 

		movement = new Vector2 (
			speed.x * inputX,
			speed.y * inputY);

		bool shoot = Input.GetButtonDown ("Fire1");
		shoot |= Input.GetButtonDown ("Fire2");

フレームごとにmovementを計算して、rigidbody.velocityに設定してあげる。(FixedUpdate()で)

指定方向にオブジェクトを動かし続けるムーブスクリプト

操作スクリプトとほぼ同じだけど、方向を示すdirectionというベクターを使って、制御する。

public Vector2 speed = new Vector2 (10,10);
public Vector2 direction = new Vector2(-1,0);

movement = new Vector2 (
			speed.x * direction.x,
			speed.y * direction.y);

体力を制御するヘルススクリプト

public void Damage(int damageCount)
	{
		hp -= damageCount;

		if(hp <= 0)
		{
			//Dead!
			Destroy(gameObject);
		}
	}

体力としてhp変数をpublicで宣言してあげる。Damage関数ではhpを減らすだけでなく、hpが0になったときにDestroyでgameObjectを破壊する処理も一緒に加えてあげるといいっぽい。

あたり判定(フレンドリーファイア判定付き)
void OnTriggerEnter2D(Collider2D other)
	{
		//Is this a shot ?
		ShotScript shot = other.gameObject.GetComponent<ShotScript> ();
		if(shot != null)
		{
			//Avoid friendly fire
			if(shot.isEnemyShot != isEnemy)
			{
				Damage(shot.damage);

				//Destroy the shot
				Destroy(shot.gameObject);
			}
		}

	}

弾が設定したコリジョンにぶつかったら呼び出される関数。今回は敵とプレイヤーで同じスクリプトを共有するので、敵同士の攻撃で敵が死なないようにフレンドリーファイア機能が必要となる。isEnemyというbool型の変数をつかって制御する。ショットスクリプト(弾)とウェポンスクリプト(発射装置)でこの変数を持っておいて、敵が撃ったときはAttack関数にtrueを、プレイヤーが撃った場合はfalseを引数として設定する。その値が弾自身のisEnemyに設定されるので、このあたり判定で敵が撃った弾なのかプレイヤーが撃った弾なのかを判定してDamage関数を呼び出す。

ウェポンスクリプト(銃弾の発射)

弾を一定の間隔で発射する

発射間隔をshootingRateとshootingCooldownという二つの変数で制御する.
銃を撃ったらある程度のクールダウンが必要で、クールダウンが終わったら次の弾を発射できるようにするという処理。

public void Attack(bool isEnemy)
	{
		if(CanAttack)
		{
			shootCoolDown = shootingRate;

			//Create a new shot
			var shotTransform = Instantiate(shotPrefab) as Transform;

			//Assign position
			shotTransform.position = transform.position;

			//The is enemy property
			ShotScript shot = shotTransform.gameObject.GetComponent<ShotScript>();

			if(shot != null)
			{
				shot.isEnemyShot = isEnemy;
			}

			//Make the weapon shot always towarts is
			MoveScript move = shotTransform.gameObject.GetComponent<MoveScript>();

			if(move != null)
			{
				move.direction = this.transform.right;
			}
		}
	}

public bool CanAttack
	{
		get
		{
			return shootCoolDown <= 0f;
		}
	}

プレイヤーとエネミーは弾を発射する時にこのAttackメソッドを呼び出す。CanAttackはゲッタメソッドで、クールダウンが終わっているかどうかを返すだけのメソッド。クールダウンが終わっていれば、新たに弾を発射するので、shootingRateをクールダウン値として設定。
弾のインスタンス化、ポジション設定をする。基本的に物体が飛んでいくという処理はムーブスクリプトが管理するので、弾の方向もMoveScriptを呼び出して、方向を制御するdirectionに値を設定する。

クールダウンはUpdateで行う。

void Update () 
	{
		if(shootCoolDown > 0)
		{
			shootCoolDown -= Time.deltaTime;
		}
	}

Time.deltatimeは前フレームからの経過時間で、それをクールダウン値から引くことで、時間経過に伴う値の減少を実現できる。よく使う書き方なので覚えておく。

ショットスクリプト(弾の制御)

弾に対して、ダメージ量と当たった相手を消す処理を与えておく。

パララックススクロール

f:id:nm777taka:20140218032938p:plain
こんな感じで背景の絵を3枚ほど複製しておく。
コレクション型のListに1枚ずつ格納していく

void Start () {
		if(isLooping)
		{
			backgroundPart = new List<Transform>();

			for(int i=0;i<transform.childCount; i++)
			{
				Transform child = transform.GetChild(i);

				//Add only the visible children
				if(child.renderer != null)
				{
					backgroundPart.Add (child);
				}
			}

transform.childCountでTransformオブジェクトの子の数(背景の3枚の絵)を取得できる。child.rendererは恐らくカメラの中にいるかどうか...(多分?)見えてる子だけをListに格納していく。そして、左から右へソートする。

backgroundPart = backgroundPart.OrderBy(
				t => t.position.x).ToList();
		}

この特殊な書き方がLINQってやつみたい。
LINQ とは Language Integrated Native Query の略称であり、.NET上でオブジェクトでも動作するデータベースのクエリの働きをするものである。強力であり、内容の理解が進むと非常に使いやすい。t=>t.position.xって部分がC#のラムダ関数と呼ばれるもの。
tってのは適当に名前をつけてるだけで、とりあえずx座標で昇順にソートした結果を.ToList()でListに格納している。Listのソートってこうやるのが定石なのかな...

ちなみにLINQを使うときは、名前空間をこんな感じに変更

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

背景画像のソートとListへの格納が終わったので、次に核となる部分の処理をUpdate関数に書いていく。

カメラを動かす

void Update () {
		Vector3 movement = new Vector3 (
			this.speed.x * this.direction.x,
			this.speed.y * this.direction.y);

		movement *= Time.deltaTime;
		transform.Translate (movement);

		//Move the camera
		if(isLinkedToCamera)
		{
			Camera.main.transform.Translate(movement);
		}

動く方向と量はvector3で定義できたので、movementに前フレームからの時間経過をかけて、動かしていく。isLinkedToCameraはpublicで定義したbool型のパラメータで、今回背景をいくつかのパートにわけ、カメラが追従しないものもあるので、このようにパラメータで制御している。
transform.Transrateでオブジェクトを相対的に動かせる。ここでは、背景が右から左へ流れていく。(進んでいるように見える!)

ループ処理
if(isLooping)
		{
			//Get the first object
			//The list is ordered from left(x posision) to right
			Transform firstChild = backgroundPart.FirstOrDefault();

			if(firstChild != null)
			{
				if(firstChild.position.x < Camera.main.transform.position.x)
				{
					if(firstChild.renderer.IsVisbleFrom(Camera.main) == false)
					{
						//Get the last child position.
						Transform lastChild = backgroundPart.LastOrDefault();
						Vector3 lastPosition = lastChild.transform.position;
						Vector3 lastSize = (lastChild.renderer.bounds.max - lastChild.renderer.bounds.min);
					
						firstChild.position = new Vector3(lastPosition.x + lastSize.x,firstChild.position.y,firstChild.position.z);

						backgroundPart.Remove(firstChild);
						backgroundPart.Add(firstChild);
					}
				}
			}
		}

うーむ、よくわからん!
べつの方法も参考にしてる本にあるので、そっちでやってみるか...

まとめ

なんかぐだぐだになってしまったけど、色々なスクリプトの作り方は勉強になったかな..笑。
次からフラッピーバード的なのを作ってみたいと思う。

Unity-4日目 2Dツールを使ってみる

f:id:nm777taka:20140213163106p:plain

Spriteでアニメーションを作る

  • 複数の画像を読み込み、Spriteに設定する。

f:id:nm777taka:20140213155838p:plain

  • CreateOtherからSpriteを作成→なんでもいいので、画像を一枚設定する。(Sprite Renderのとこ)
Spriteにコマアニメを設定
  • 作成したSpriteにAddコンポ→Miscellaneous→Animator
  • Animation Viewを開いて、新規アニメーションクリップを作成

f:id:nm777taka:20140213160828p:plain

  • Add Curve→Sprite Render→Sprite
  • 画像を並べてく
アニメーションの切り替え(Mecanim)

今回は鳥が飛ぶアニメーションと走るアニメーションの2つを切り替える。
切り替えるポイントは地上にいるか、空中にいるか。つまり地面との衝突判定を見る。

  • bool型のパラメータを新たに追加
  • アニメーションボックス右クリックからMake Trantisionで遷移先のアニメーションboxへ引っ張る
  • 矢印を選択し、Conditionsのパラメータを先ほど作ったbook型のものに変更

f:id:nm777taka:20140213161435p:plain
あとはこのbool変数をスクリプトから設定してあげる

スクリプトによるアニメーション制御

とりあえず、SpriteにboxColliderとcircleColliderを設定。
(地面との衝突判定はサークルを使って、上半身はボックスで囲むといいらしい)
f:id:nm777taka:20140213161749p:plain

地面にいるかどうか判定するスクリプト
void Update () {
		bool grounded = Physics2D.Linecast (
			transform.position + transform.up * 1, 
			transform.position - transform.up * 0.1f,groundLayer);

		Debug.Log ("grounded - >" + grounded);

		GetComponent<Animator> ().SetBool ("Grounded", grounded);
	}

Physics2D.Linecastで判定をするんだけど、イメージは自分のpositionのちょっと上からちょっとしたまで線を引っ張って、その線上にコライダーが合ったら衝突判定をするって感じらしい。
しかし、Sprite自体にもコライダーがあり、地面に設定したコライダーと区別が使いないので、
LayerMaskというものを設定してあげる。

public LayerMask groundLayer;

そしてこのground Layerの値をインスペクターからデフォルト(別々になればOK)
Sprite自体は新たにレイヤーを追加("character"みたいな)

linecastの引数にこのマスクを一緒に渡してあげれば、地面のコライダーとのみ判定を行う。

値を設定

GetComponet~で先ほどMecanimで設定したGrounded(bool型)に値を設定してあげる。

キャラを動かす

とりあえず、rigidbody2Dを追加。Fixed Angleで角度を固定して、Interpolateを設定(前フレーム補完でスムーズになるっぽい?)
あとは、addForceで上下左右に動かす。物理挙動なのでFixedUpdate()に書く。

void FixedUpdate() {
		float h = Input.GetAxis("Horizontal");
		float v = Input.GetAxis ("Vertical");

		if ((h > 0 && !facingRight) || (h < 0) && facingRight) {
			facingRight = (h > 0);
			transform.localScale = new Vector3((facingRight ? 1 : -1),1,1);
				}

		if (rigidbody2D.velocity.x < maxWalkSpeed) {
			rigidbody2D.AddForce (Vector2.right * h * walkForce);
				}

		if (v > 0 && rigidbody2D.velocity.y < maxFlySpeed) {
			rigidbody2D.AddForce(Vector2.up * v * flyForce);
				}

		if (Mathf.Abs (rigidbody2D.velocity.x) > maxWalkSpeed) {
			rigidbody2D.velocity = new Vector2 (
				Mathf.Sign(rigidbody2D.velocity.x) * maxWalkSpeed,rigidbody2D.velocity.y);
				}

	}

facingRight(bool)のとこはキャラが移動方向に合わせて振り向く処理。
Spriteの場合はtransform.localScaleを参照するらしい。
Addforceの場合、力を加え続けることになるので、どんどん加速してしまう...
よって最大速度制御が必要になる。
最大速度を超えていた時は、Mathf.Signで符号を取得し、最大速度を設定する。

Unity-3日目 コインドーザーに挑戦

とりあえずこんな感じ

f:id:nm777taka:20140212200758p:plain

コイン投入処理

まず空のgameObjectを作って、スクリプトを追加

function Update () {
	if(Input.GetButtonDown("Fire1")) {
		var offs = Vector3(Mathf.Sin(Time.time * 7),0,0);
		Instantiate(prefab,transform.position + offs,transform.rotation);
		Score.score -= 1;
	}
}

コインをプレハブにしといて、インスタンス化の流れはこれまでと同様

x軸方向にランダムに動かす

Math.f.Sin関数を使うと簡単に実現できる。これ便利!!

コインのあたり判定

コイン(シリンダーオブジェクト)にメッシュコライダーを追加
メッシュコライダーのConvexにチェックすると、そのメッシュを包み込むようなあたり判定になる!

プッシャー

var origin : Vector3;

function Start () {
	origin = rigidbody.position;
}

function Update () {
	var offs = Vector3(0,0,Mathf.Sin(Time.time));
	rigidbody.MovePosition(origin+offs);
}

こちらもMathf.Sinで周期的に動かす。
rigidbody.MovePositionでrigidbodyを追加したオブジェクトを動かせるみたい。
初期位置のoriginにはゲーム開始時のrigidbodyのpositionを保存しておく。

スコア処理

f:id:nm777taka:20140212202346p:plain
こんな感じで、空のゲームオブジェクトを設定する。
このゲームオブジェクトとの衝突判定でスコアを加算し、コインを消す。

unction OnTriggerEnter(other:Collider) {
	Destroy(other.gameObject);
	
	if(other.gameObject.tag == "star"){
		Score.score += 5;
	} else if (other.gameObject.tag == "candy"){
		Score.score += 10;
	} else if (other.gameObject.tag == "snow"){
		Score.score += 20;
	} else {
	
		Score.score += 2;
	}
	
	
}

今回はコインだけでなく、キャンディーやら星やらのオーナメントを用意したので、
tagによって点数を分ける。

画面に点数を表示

空のgameObjectを作って、GUITextを追加する。
あとはスクリプトからいじるだけ。

static var score : int;

function Awake () {
	score = 30;
}

function Update () {
	guiText.text = score.ToString();
}

開始時に持ち金を設定する。
guiText.textで先ほど設定したGUITextにテキストを設定できる。
score.ToString()でint型をstring型に変換し、設定すればスコアが表示される。

まとめ

とりあえず基本的な部分は完成したので、ゲームとして楽しめるようにしてきたい。
今回使ったMathf.SinとかGUITextはこれからも使う機会がおおいと思うので、覚えておく。