Yaminabe

らくがきぶろぐ

てすと:Yaminabeちゃんは生まれたばかりのブログです みんなで仲よく使ってね

カテゴリ: Unity3d

sutopann



ストパヌSTG たしか4回目ぐらいのテストです。

windows用:   https://dl.dropboxusercontent.com/s/7rh61f3f6hxp18o/SW111213.app.zip

Mac用:    https://dl.dropboxusercontent.com/s/7rh61f3f6hxp18o/SW111213.app.zip



解像度はノートの画面サイズ1280×800を基準に設定しています。画面レイアウトを崩さないために
固定サイズで遊んでください。


■操作方法は前回と同じく

カーソルキー = 移動

右CTRLキー = オートエイムのオンオフ
Zキー      =ショット
Xキー      =固有魔法(まだ 未対応のものあり)
左CTRLキー  =スピードアップ
SPACEキー  =チームの分散と集合
Bキー      =チーム内のキャラクターチェンジ     
0(ゼロ)キー   =雲のOFF
Pキー      =ポーズ

O(オー)キー  = ネームタグのオンオフ
7キー  =  他の面の敵(ザコキャラ)を追加します
8キー  =  テスト用に背景表示切替となっています(ただし海面がシェーダー不備の関係でおかしくなります)
9キー  =  テスト用各面ボス表示

Cキー は自爆スイッチです。 ゲームオーバーのテスト用につけてます。


今回は3ステージまで進むことができます。強制的にゲームオーバーになりますが次回バージョンでは
少し変化を付ける予定です。

今回の変更点としてネウ子のほかに服部さんを選択することができますが 性能は坂本さんとほぼ同等で
魔法が使えないものです。

各面の構成は今のところほぼ同じで味気ないです。 いろいろつめなきゃいけない部分はあるのですけど
とりあえず全体の流れをつくってしまわないと先に進めないので そいういうものだと思ってください。

あいかわらず雲があることで難しさがアップしています(+重たい)ので雲を消してしまうと簡単だと思います。
いまはボスの硬さに変化をつけていないのでだいぶ簡単に進むことができるのでは。

各ステージのBGMなんかもとりあえず入れてみたのですけど うーんどうなんだろうw 
自分で作曲しておいてなんですけど 曲作りのセンスはもうちょっとかな 勉強しないと。 
特に海のステージのは最初に作った曲なので いま聞くとなんだか恥ずかしいです。 
まあ シューティングってたいていどんな曲でも合っちゃうんですけどねw
問題は素材の味を活かせているかというところです。

プレイしてみて改良点なんかは 歓迎しますのでどうぞコメしてやってください。

追記: ゲーム本体が大きくなるにつれて意味不明のエラー?が出るようになって来ました。
具体的には Update文中にプレイヤキャラクターを整列させる関数をはさんでみたところ オートエイムが効かなくなり
Awakeに同じ関数を2重に書いてやると(実行する意味はまったくないです)なぜか問題が解決したりと 
よくわからないことが起きはじめていて 開発がちと難しくなっている現状です。

ゲームのバランスを取るのはこれからですが、劇場版の時期とか関係ないんで 飽きるまでやりますw 


あと マウスで照準をつけるのもテストでやってみたんですけど 難しすぎワロタ状態だったので封印。

ランキングという検索が多かったので うちのストパンSTGでランキングをどうやっているかを
簡単に書いておきます。

だいたいPlayerPrefsを使用するとデータを外部に保持できるということは以前おはなししましたので
どうやって使用するか。

おおまかな流れとしては

◯ランキングの初期化

◯ネームエントリー

◯ランキングのソート

◯ランキングの表示


これらがあればスタンドアローンでの表示は大丈夫だと思います。
はじめは2分木ソートなどの関数を使用していたのですが、ランク値の個数が少ないのと デバッグが大変になったので、 簡単にまとめてみた感じです。
急ごしらえでバグあるかも知れないですけど なおしたらこっそりおしえてくださいw


PHP等を使用したネットランキングの場合は またちょっとややこしいので今回は割愛します。

あと どこかにキーを削除する関数を設けて ランキングの初期化をしたほうがデバッグ時にも重宝しますので
挑戦してみてください。

値を選択して消去する場合は PlayerPrefs.DeleteKey ("キーの名前”);
値をすべて消去する場合  PlayerPrefs.DeleteAll  



■ランキングの初期化

private var Score:int[]=[100000,80000,60000,50000,40000,30000,20000];
private var Name:String[]=["AAA","BBB","CCC","DDD","EEE","FFF","GGG"];
private var Class:String[]=["FIGHTER","FIGHTER","FIGHTER","FIGHTER","FIGHTER","FIGHTER","FIGHTER"];

まずはじめにダミーデータを用意してランキングを初期化しておきます。

function Awake(){



if(PlayerPrefs.GetInt("Score1") == null) SetData();

Score1という値が入っているか(初回起動かどうか)をチェックして 値が入っていなければ
初期化の関数『SetData()』を呼びます。
初回起動チェックなので なにかフラグを値として入れておいても構いません


}



function SetData()
{


PlayerPrefsを使ってレジストリにスコアの初期値を書き込みます
ランキングに表示される個数分の値を記述します。



PlayerPrefs.SetInt("Score1",Score[0]);
PlayerPrefs.SetString("Name1", Name[0]);
PlayerPrefs.SetString("Class1", Class[0]);

PlayerPrefs.SetInt("Score2",Score[1]);
PlayerPrefs.SetString("Name2", Name[1]);
PlayerPrefs.SetString("Class2", Class[1]);


・・・・・・


}





■ネームエントリー

function Awake(){



var playerNameInput:String = "AAA";


var ScoreTmp:int = PlayerPrefs.GetInt("Score7");


if(ScoreTmp <= Total_Score )
{
requirePlayerName=true;

}else { Application.LoadLevel("Ranking");}

一番最下位のランキングスコアとゲーム終了時のスコアを比較して条件が合えば
    ネームエントリーのテキストフィールドを表示 
    それ以外の場合『Ranking』のシーンを読み込み


}


function OnGUI(){

if(gSkin)
GUI.skin = gSkin;
else
Debug.Log("StartMenuGUI : GUI Skin object missing!");




if(requirePlayerName){
myWindowRect = GUILayout.Window (9, Rect(Screen.width/2-150,Screen.height/2-100,300,100), EntryNameMenu, "enter name");
}




}





function EntryNameMenu(id : int){

GUILayout.BeginVertical();
GUILayout.Space(10);



GUILayout.BeginHorizontal();
GUILayout.Space(10);
playerNameInput = GUILayout.TextField(playerNameInput);
GUILayout.Space(10);
GUILayout.EndHorizontal();




GUILayout.BeginHorizontal();
GUILayout.Space(10);
if(playerNameInput.length>=1){
if(GUILayout.Button("entry")){
requirePlayerName=false;
PlayerPrefs.SetString("playerName", playerNameInput);
Application.LoadLevel("Ranking");
}
}else{
GUILayout.Label("Enter name to continue");
}
GUILayout.Space(10);
GUILayout.EndHorizontal();


GUILayout.EndVertical();

}






■ランキングのソート


function RankDataSet()
{
var DATA_SIZE:int;
var insertData:int;





Score[0] = PlayerPrefs.GetInt("Score1");
Name[0] = PlayerPrefs.GetString("Name1");
Class[0] = PlayerPrefs.GetString("Class1");

Score[1] = PlayerPrefs.GetInt("Score2");
Name[1] = PlayerPrefs.GetString("Name2");
Class[1] =PlayerPrefs.GetString("Class2");




.........................

スコアの表示数分記述







DATA_SIZE =Score.length;
insertData = Total_Score ;

『Total_Scoreはゲーム終了時のスコアでソートのキーになります』




var min:int;
for(var i= DATA_SIZE-1;i>=0;i--){
if (Score[i] < insertData) min=i;

}



for(var ii= DATA_SIZE-1;ii>min;ii--){

Score[ii]=Score[ii-1];
Name[ii]=Name[ii-1];
Class[ii]=Class[ii-1];

}
Score[min] = insertData;
Name[min] = PlayerPrefs.GetString("playerName");
Class[min] = ClassDataSet();


1:スコアとインサートデータ(今回のゲームの終了時のスコア)を比較して
スコアのデータ数分だけのソートを行います
変数『min』には比較結果のランクが入ります。

2:for文でminの位置までひとつづつランクデータを下にずらします

3:minのランクにインサートデータとネームエントリーで入力された名前をセットします

4:PlayerPlefsをつかってソート終了のデータをレジストリに(スタンドアローンの場合)書き込みます






PlayerPrefs.SetInt("Score1",Score[0]);
PlayerPrefs.SetString("Name1",Name[0]);
PlayerPrefs.SetString("Class1",Class[0]);

PlayerPrefs.SetInt("Score2",Score[1]);
PlayerPrefs.SetString("Name2",Name[1]);
PlayerPrefs.SetString("Class2",Class[1]);

スコア表示数分記述・・・・・・・・・・・・・・・




return ;
}




■ランキングの表示


function OnGUI () {




if(gSkin)GUI.skin = gSkin;
else
Debug.Log("StartMenuGUI : GUI Skin object missing!");


GUI.contentColor = Color (0.75, 0.0 ,0.0);

var backgroundStyle : GUIStyle = new GUIStyle();
backgroundStyle.normal.background = backdrop;
GUILayout.Label ("Press Anykey to Gamestart");


isLoading = true;


if (Input.anyKeyDown) Application.LoadLevel(0);





GUI.Label( Rect( Screen.width/2-340, 240, 128, 32),"RANK" );
GUI.Label( Rect( Screen.width/2-164, 240, 164, 32),"SCORE" );
GUI.Label( Rect( Screen.width/2+60, 240, 164, 32),"NAME" );
GUI.Label( Rect( Screen.width/2+240,240, 164, 32),"CLASS" );




  GUI.Label( Rect( Screen.width/2-340, 300, 128, 32),"1ST" );

GUI.Label( Rect( Screen.width/2-340, 360, 128, 32),"2ND");

GUI.Label( Rect( Screen.width/2-340, 420, 128, 32),"3RD" );

GUI.Label( Rect( Screen.width/2-340, 480, 128, 32),"4TH");

GUI.Label( Rect( Screen.width/2-340, 540, 128, 32),"5TH");

GUI.Label( Rect( Screen.width/2-340, 600, 128, 32),"6TH");

GUI.Label( Rect( Screen.width/2-340, 660, 128, 32),"7TH");




GUI.contentColor = Color.white;
  GUI.Label( Rect( Screen.width/2-164, 300, 164, 32),""+ Score[0]);

・・・・・・・・・・・・・・・・・・・・・・・・・・
GUI.Label( Rect( Screen.width/2-164, 660, 164, 32),""+ Score[6] );



GUI.contentColor = Color.white;
GUI.Label( Rect( Screen.width/2+60, 300, 164, 32),Name[0] );

・・・・・・・・・・・・・・・・・・・・・・・・・・
GUI.Label( Rect( Screen.width/2+60, 660, 164, 32),Name[6] );




GUI.contentColor = Color.white;
GUI.Label( Rect( Screen.width/2+240, 300, 196, 32),Class[0]);

・・・・・・・・・・・・・・・・・・・・・・・・・・
      GUI.Label( Rect( Screen.width/2+240, 660, 196, 32),Class[6]);



}








111125

arctorkさんからのリクエストでうちのとこの看板娘を描いて欲しいということだったので 描いてみたてすとです。
当初 カポエラさんという(勝手に名づけましたすいません)カポエラをやっている女性を描こうとしていたのですが、
絵が小さくて表情がよくわからないので 自転車の女性絵のほうを描いてみました。 ほうれい線の入れ方に苦労されているようだったので、いちおう入れて見ました まあこんな感じでアッサリ描けばいいのですよ。
いちおう笑わせて ほうれい線が出る部分を強調してみたのですが、『健康優良不良中年』になってしまったかも知れません(不良中年の元ネタはわかりますよね)。
しかし背景まで手が回りませんでした と言うかゲーム制作の寝不足でちよっと今回ギブアップです。
ちなみにバッグについてるのはルアーです 針のないやつね(リップつけ忘れた)。 なんとなく笑顔にしたらアウトドア派かもしれないとおもいつけて見ました。旦那さんがいる程度の年齢なので旦那の趣味のルアーを『これ綺麗じゃない』っていいつつ エコバックにつけて買い物に出かけるの図。

sw_hattori


ストパンSTGの進捗です。 すいませんねUnity記事を期待してる方 もうしばらくゲーム制作のほうが忙しいとこなので、講座関連はちょっと待って下さい。
んで
服部さんです。 発表のすぐ後にポリゴンモデルは出来てたので 動かして見ましたよというところ。
魔法使えるかどうかもわからんのですが 坂本さんコンパチでいいんじゃない(外見似てるし)ということで
モデルだけすげ替えた感じです。 能力とか発表されてないからさっぱりわかんないので 
これ以上つくるのは無理無理無理無理かたつむり。

sw_warlock


youtubeの方でアメリカ人の学生さん(19)に連コメで 『ウォーーロック入れろよ あれかっこいいだろ 糞が』
(※意訳です)と激しいツッコミを入れられたのでモデル作りました 変形もします。
だけどこれから攻撃ルーチンとかつくってたら とても劇場版の時期に間に合いそうも無いのでとりあえず飛んでるだけです。
ついでに同じ方の『なんで ネウ子がプレイヤーなんだよ あいつは悪もんだろ 糞が』(※意訳です)というコメもありましたが、こころのなかで、もう一人の私が『脚本の意図も読みとけねえのか糞アメ公 ディズニーでも見てマスかいてろ』とつぶやいて無視してしまいました。 わたしではありませんよ もう一人の私です。

そういえば関係無いですが ロアナプラは水の底に沈んじゃってるらしいですね

つかれたので『聡美発見伝』でも聞きながら眠りにつきます さばら


すっかりご無沙汰していましたがトラックバックをつけてもらいました オブジェクトの非表示を
子供だけに行いたいということらしかったのですが 説明が不足してました
ゲームオブジェクトの親子の操作方法についてお話しします。

var object:GameObject = gameObject.find("オブジェクトネーム")
これは ゲームオブジェクトを取得することができます。
objectの階層下の子供を取得したい場合は
transform.Find("子供の名前")を使用します。
階層が深い場合は
transform.Find( 直下の子供の名前/・・ /取得したい子の名前)というように
ツリー構造をファイル操作のディレクトリと同じように記述します。
例)
function Update() {
aFinger = transform.Find("LeftShoulder/Arm/Hand/Finger");
aFingerに対する実行文
}

指定オブジェクトに操作をおこなうときはgameObject .transform.Find()のように 指定するゲームオブジェクトの後につづけて記述します。

で 問題の点ですがGameObjectを消さないで操作を行いたいという点です。
gameObject.SetActiveRecursively(false);
を使用するとgameObjectの階層は全て消えてしまいますので次回から
Findでのサーチができなくなるという点です
そこでtransformの下にはgameObject自分本体の指定が格納されているということを利用して
transform.gameObject.SetActiveRecursively(false);
を使用したらよいのではということですね これでも問題ないと思います。
わたしの場合は高速化のためにゲーム中で低速なFind関数を使用せずにstart()の中で一度
var Player:GameObject;
start(){
Player = GameObject.Find("player");
Player.SetActiveRecursively(false);
}

として表示の時は
Player.SetActiveRecursively(true);
というように変数に一旦格納して表示させるのでこのような問題にあったことがありませんでした。

ほかにもプレファブを使用する場合は
var prefab:GameObject;
var object:GameObject = Instansiate(prefab,transform.position,transform.rotation);

objectはプレファブのコピーです
などというように 一度配列や変数にとってしまったほうが効率よくスクリプトかけるような気がします。
注)SetActiveRecursively()はRendererがコンポーネント登録されていないと機能しません。

注目点はTransform型はGameObject型の代わりに使用することができて
transform.gameObject と gameObject(this.gameObject)
 が同じものを示しているというところです。
よくUnityのスクリプトでvarにモデルを登録するのがGameObjectではなくTransformだったりするのはtransform.gameObjectで代用できるからなのですね。




ゲームオブジェクトのtransformの下にはrootやparentといった階層のルート(一番上の階層)のtransformや親階層(階層の一つ上)のtransformが格納されています。

例えば transform.parent = transform
とすれば 親子構造を作ることができます。この場合transformはゲームオブジェクトの自分自身です 
親から切り離されたいときは transform.parent = null;
としてやればよいです。

他にも例えば 子を破壊せずに切り離したいときはTransform.DetachChildrenを用いて
transform.DetachChildren();
Destroy(gameObject);

のようにします。 これは公式のFPSチュートリアルでロケットランチャーから爆発前にトレイル煙を切り離す部分に使用されています。

■子の検索
子の検索はGetComponentInChildren()を使用して

var allChildren:Transform[] = gameObject.GetComponentsInChildren(Transform);
for (var child : Transform in allChildren) {
    実行文
}

のように行います。allChildrenはTransformの配列型指定である点に注意してください。
何度も呼び出されるこのような実行部分はstatic関数にしてしまうと効率が良いです。
例)
static function FindTransform(parent:Transform, name:String)
{
if (parent.name == name ) return parent;
for (var child in parent)
{
result:Transform = FindTransform(child, name);
if (result != null) return result;
}
return null;
}



そして例えば子だけを削除してしまいたい場合は

for(var child in allChildren)  Destroy(child);

Destroy()文をかえれば

スクリプト系の場合
child.active = false;

または

Rendererなど表示系の場合
child.enabled = false;


万能な非表示系
child.SetActiveRecursively(false);

というように子を非表示にすることが出来ます。

ここでallChildrenは配列型ですが Transform型かGameObject型かで書き方を
変えなければいけません。

例)
var allChildren:Transform[];
allChidren = gameObject.GetComponentsInChildren(Transform);

for (var child in allChildren)
{
Destroy(child.gameObject);
}


この場合Transform型でallChildrenに登録していますので child=子 はTransform型なので.gameObjectをつけてオブジェクト本体を指定します。


その他
■タグのチェンジ 
ゲームオブジェクトのタグを変更する場合。インスペクタに
登録されているタグを付け替えることができます。
gameObject.tag = "Player";
または
transform.gameObject.tag = "Player";
など 同じオブジェクトを用いて敵と自機を分けたいときなどに使用出来ます。


■レイヤーチェンジ

例)
function ChangeLayersRecursively(var trans : Transform, var name : String)
{
for (var child : Transform in trans)
{
child.gameObject.layer = LayerMask.NameToLayer(name);
ChangeLayersRecursively(child, name);
}
}

この例のスクリプトをどこで見たのか忘れてしまいましたがLayerMask.NameToLayerを使用してオブジェクトのレイヤーも変更することができます。
この例ではtransのに子のTransformをいれてレイヤーの名前をnameにいれて
コールするとレイヤーを変更します。for文の中で再帰的な呼び出し(関数が自分自身を呼び出すこと)をしています。



ということでした。久しぶりの記事なのでちょっとボリュームたりませんでしたかね?
記事の文中おそらく間違えているところも色々あると思いますので指摘していただいてかまいませんよー。
ちなみにどこかのブログで現役プログラマーと紹介されていましたが わたしCGデザイナーです。

あとこのブログはライブドアのテンプレートで書いているのですがフォントサイズが小さいという指摘を
以前受けました。現在たいていのブラウザは ctrl+”+” で記事を拡大することができますので試してみてくださいね。

9月に入りました ってもう 8日です。
急に涼しくなって 体は元気になったのですけど 開発の方はそれほど進んでいるわけでもなく
ハードディスクがデータを持って行ってしまった関係で ちと記事の更新が滞っています もうしわけないです。

そんな状況ですが Dota系ゲームプロジェクトのほうが少し進展していますので 進捗報告です。

とりあえず今週ネットワーク対応しました。

というかUnityのネット関連は はじめわさっぱりわからなくて、胃ががキリキリしたんですが
いくつかサンプルを見ながら試してみたら するっと出来てしまって拍子抜けしたところです。
Unityでは多くの場合マルチプレイはSmartFoxという無料サーバーソフトを使用するようです あと Photonとかね。 フリー版でも20人まで入れるんですよー マルチプレイは多すぎてもゲームにならないですから十分じゃないかな。
なんですが 専用の関数使うんですよね。 移植するときに大変だから 結局Unityのネットワーク命令使って汎用性のほうを重視してみたり。 といってもSmartFox使用する場合もプラグイン入れて
ライブラリを呼んでやるだけなので 難しいかというとそれほどでもない  っぽんですけど。。



今回はとりあえずキャラクタの絵を描いてロビー画面に表示部分をでっち上げました。
まだキャラクタセレクトなどはついていませんが、ロビーチャット と プレイヤー登録はできます。
クライアント側からは自動でサーバーを見つけてくれるらしく サーバー側がどこでプレイしていてもロビーに
参加できるようです。この仕組みはよくわかりません が できているみたい。
もちろんゲームも出来ますよ。

基本的にNetworkViewというスクリプトをinspectorに登録してやることでネット越しにデータの受け渡しが可能になると言う所だけおさえておけば なんとかなります。たぶん
ここらへんは 完成したら解説を入れようかいなと思っています。。 ただ わたし忘れっぽいので
訪ねてもらわないと思い出せないかも知れません。

Unityのネットワークサンプルでは 移動のスクリプトを二種類持って 自分がコントロールしているときに
NetworkView.isMineという値を見て キー入力でキャラクタを動かすスクリプトと サーバー側からデータを読み取ってキャラを移動するスクリプトを切り替えています。 が キャラが増えてくるとセットアップがめんどくさくなるのでひとつのスクリプトにまとめてしまいました。 ここらへんがストパンSTGで学んだところw 必ずしも
オブジェクト指向っぽい作りが効率がいいというわけではないので あまりスクリプトを細かく分けても管理がめんどくさい。

dotaTest2


dotaTest1


そして 以下がミニオンという 上の画面でオタマジャクシみたいなやつです (要するにザコキャラ)を
コントロールするためのテストプログラムです。 本来は前に進み始めたら逆進したらいけないのですが
どうどうとやってくれちゃってます。  これは Aスターの更新タイミングが上手くいっていなくて 敵を倒した後に
スタート位置からの追跡のテーブルが更新されずに残っているんですね で スタートに戻ってしまっていると
まあ 敵を倒したら新ルートを探すように変更すればいいのですけど いろいろあって手付かずになってます。

BoidsTest2


https://dl.dropboxusercontent.com/s/6ehopar42j6s5ap/WebPlayer.unity3d


その他:ストパンSTGのほうは また放置状態になっていますが 実は今回のネットワークの勉強を参考にして
対戦?というか アニメでやってた模擬戦をゲームにできんかなあ と ちょっと模索してるところです。
が なかなか仕事の合間につくるのも大変でして・・ どうなるか約束はできません


ということで また Unity記事でしたが
実はうちのブログで一番アクセスがあるのはアニメ感想という事実
『2011 春アニメ 感想』やら『2011 夏アニメ 感想』なんていうキーワードが一番多いんですね
あとひと月でまた 秋アニメ感想を書かねばならんのかー 体力回復せねば
 

↑このページのトップヘ