一覧表示(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)
- 設定より規約
基本的なコマンド
作成手順
まずは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
ルーティングを表示する。
ブラウザがURIにアクセスして、右側のControllerアクションを実行していく。
とりあえず今日はここまで。
次回から各アクションを作っていく。
適当につくったフラッピーもどきを公開
https://dl.dropboxusercontent.com/u/203783282/FlappyBird/FlappyBird-web/FlappyBird-web.html
画像素材→GithubよりAngryChicken2D内のテクスチャより
音素材→魔王魂様より(http://maoudamashii.jokersounds.com/list/song2.html)
Unity-6日目 フラッピーバードを作ってみる
今話題のフラッピーバードを作ってみたー。Unityが神すぎて意外に簡単に作れたので手順を残しておく。
スクリプト一覧
- PlayerControl : 鳥をクリックでジャンプさせる
- MoveScript : 障害物を移動させる
- WallManager : 障害物を発生させる
- CubeScript : あたり判定
- JudgementScript : クリア判定
- ScoreManagerScript : スコア変数の管理
- ScoreScript : スコアの加算と標示
- Title,EndScnecScript : GUIボタンと画面遷移
画面構成
- 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のパラメータ設定
デフォルトの設定だと、ふわふわしてしまったので、適当にパラメータをいじった。
重力と質量を増やして、それっぽい動作になった。
障害物を発生させる
ゲームの仕組みは、鳥はその場でジャンプするだけで、障害物が右側から移動してくるというもの。その障害物を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に登録してあるシーンは上から順番に番号がふられる)
クリア判定
障害物の間を通ったかどうかを判定するために、新たにクリア判定用の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スクリーンにタイトル画面から遷移する処理は上の感じ.
完成!
これにいい感じの素材つけたら、フラッピーの完成だあああ!!
Unity-5日目 2Dスクロールゲーム
よくある弾幕ゲームみたいなの
ポイントは背景が無限に続いていくパララックススクロール?
スプライトの基本的な扱いは前回と一緒だけど、パララックスやカメラ追従が加わってる感じ。
とりあえず、チュートリアル見ながらやってみた(英語だったから正確かどうか怪しいけど)
制作の過程で得た気づきとか学んだ事を書いていこうと思う。
スクリプトの説明
- PlayerScript→操作、攻撃
- MoveScript→オブジェクトが自動的に移動する処理
- HealthScript→体力制御
- ShotScript→銃弾の制御(ダメージ量、オブジェクト破壊)
- WeaponScript→銃弾発射の制御(クールダウンなど)
- EnemyScript→攻撃
一枚の絵から複数のスプライトを生成する
例えばこのように一枚の絵を複数のパーツに分けたい時は、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は前フレームからの経過時間で、それをクールダウン値から引くことで、時間経過に伴う値の減少を実現できる。よく使う書き方なので覚えておく。
ショットスクリプト(弾の制御)
弾に対して、ダメージ量と当たった相手を消す処理を与えておく。
パララックススクロール
こんな感じで背景の絵を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のソートってこうやるのが定石なのかな...
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ツールを使ってみる
Spriteでアニメーションを作る
- 複数の画像を読み込み、Spriteに設定する。
- CreateOtherからSpriteを作成→なんでもいいので、画像を一枚設定する。(Sprite Renderのとこ)
Spriteにコマアニメを設定
- 作成したSpriteにAddコンポ→Miscellaneous→Animator
- Animation Viewを開いて、新規アニメーションクリップを作成
- Add Curve→Sprite Render→Sprite
- 画像を並べてく
アニメーションの切り替え(Mecanim)
今回は鳥が飛ぶアニメーションと走るアニメーションの2つを切り替える。
切り替えるポイントは地上にいるか、空中にいるか。つまり地面との衝突判定を見る。
- bool型のパラメータを新たに追加
- アニメーションボックス右クリックからMake Trantisionで遷移先のアニメーションboxへ引っ張る
- 矢印を選択し、Conditionsのパラメータを先ほど作ったbook型のものに変更
あとはこのbool変数をスクリプトから設定してあげる
スクリプトによるアニメーション制御
とりあえず、SpriteにboxColliderとcircleColliderを設定。
(地面との衝突判定はサークルを使って、上半身はボックスで囲むといいらしい)
地面にいるかどうか判定するスクリプト
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日目 コインドーザーに挑戦
とりあえずこんな感じ
コイン投入処理
まず空の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を保存しておく。
スコア処理
こんな感じで、空のゲームオブジェクトを設定する。
このゲームオブジェクトとの衝突判定でスコアを加算し、コインを消す。
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はこれからも使う機会がおおいと思うので、覚えておく。