黒の時代

弾幕風ph3初心者による弾幕風講座。まだ難しいことは出来ませんが、質問要望あればコメントにどうぞ。

no.0 目次

ごあいさつ(0ω0)ノシ

ph3に即した弾幕風講座が見つからないので、自分で作ることにした弾幕風初心者です。頑張ってエフェクトマスターして講座書いてドヤ顔したいです。

目次

no.1 必要なもの、用意するもの
no.2 テンプレートとざっっっくり解説
no.3 @MainLoops、if文、変数を使う、弾を出す!
no.4 nway弾に学ぶloop、while、ascent、function
no.5 難敵()マイクロスレッド
no.6 「諏訪大戦」でマイクロスレッドに慣れる
no.7 続・「諏訪大戦」弾オブジェクトを導入
no.8 敵を動かすの忘れてたorz

ごあいさつの続き

書いてる人(黒の時代)は0.x系からのユーザーです。無意識に説明を飛ばしてたりするかもしれません。というか自分で言うのもなんなんですが、説明はかなり不親切です。聞きたいことがあればコメントにどうぞ。

それから、黒の時代は情弱です。プログラム言語的な説明の正確さは保証しません。てゆか一切の責任は負いません。PCぶっ壊れたらごめんなさい。

 

あと僕の作った素材は著作権放棄というか無断使用転用して構いません。クレジット表記もいりません。そんな真面目に作ってないし。

 

※変数の名前とかは、no.2のテンプレを見てください。

no.9 魔方陣 ぐるぐる。

方陣でも出しましょう。永夜抄弾幕を再現…はしませんめどい

レンダーなオブジェクトの一番簡単なバージョンです。

 

使用する魔方陣画像はこちらです。

f:id:chabrier:20140504130100p:plain

即席自作です。あんまりきれいじゃないけどそこは本題ではないので無視してくださいな。

NO.8 敵を動かしてみよう(忘れていたわけじゃない←)

敵を動かすの忘れてました。
いえ、忘れてたわけじゃありません。めんどくさかっただけです。

我らがテンプレにはすでに@Initializeに
ObjMove_SetDestAtFrame(objEnemy, cx, 130, 60);
ってあると思います。コイツで動かします。

あ、ちなみにobjEnemyは我々が勝手に名前をつけた変数ですよ。

スクリプトの最初で
let objEnemy;って宣言して、
@InitializeでobjEnemy = ObjEnemy_Create(OBJ_ENEMY_BOSS);
ってボス敵のIDを代入してますね。

ここらへんも0.x系との違いですね。0.x系はボスはボス専用の関数で動かしていましたが、ph3では一般のオブジェクト用の関数で動かしていきます。ちょっとめんどくさい感じがしますが、敵を数体出せるようにするための仕様変更だそうです。

代入無しの宣言を最初に行っている理由は、
代入が行えるのはボスを作成する@Initializeだけだけど、ここでletで宣言してしまうと、objEnemyって変数がグローバルなものでなくなってしまう = @Initialize内でしか使えなくなってしまうからですね。

さて東方のボスキャラは、固有の動きをするものを除いては、
自機がボスより右にいる→右に動く
自機がボスより左にいる→左に動く
って感じで周期的に動きますね。

それともうひとつ条件が。ボスが画面端近くに居る場合は、ちょっと中央に戻ります。

この4種類の分類をif文で作ってみましょう。まずは自分で考えてみてください。






…画面端の条件の方が、自機とボスの相対位置の条件より優位にありますよね。そこに気をつけて組みます。
画面端の座標は、左が0で右がGetStgFrameWidth()です。exと同じようにスクリプトの最初でlet rightx = GetStgFrameWidth;とか書いておいてもいいと思います。

if(GetStgFrameWidth-ex<60) {左にちょっと動く}
else if (GetPlayerX>ex) {右に動く}
if(ex<60) {右にちょっと動く}
else if (GetPlayerX>ex) {左に動く}

が答えです。60は適当。

ObjMove_SetDestAtSpeed
ObjMove_SetDestAtFrame
ObjMove_SetDestAtWeight

3種類あるので、用途に応じて使ってみてください。

今回はこれだけです。

no.7.1 諏訪大戦コピー

>|c| 

#東方弾幕風[Single]
#ScriptVersion[3]
#Title["諏訪大戦劣化コピー@黒の時代"] 
#Text[""] 

#include"script/default_system/Default_ShotConst.txt"  

let imgExRumia = GetCurrentScriptDirectory~"ExRumia.png"; 

let objPlayer = GetPlayerObjectID();
let objEnemy;
let frame = 0;
let ex; let ey; let cx = GetStgFrameWidth() / 2;
let right = GetStgFrameWidth;
let bottom = GetStgFrameHeight;

@Event {  
   alternative(GetEventType())
   case(EV_REQUEST_LIFE) { SetScriptResult(3000); }
}

@Initialize {
   objEnemy = ObjEnemy_Create(OBJ_ENEMY_BOSS);
   ObjEnemy_Regist(objEnemy);  
   ObjPrim_SetTexture(objEnemy, imgExRumia); 
   ObjSprite2D_SetSourceRect(objEnemy, 64, 1, 127, 64); 
   ObjSprite2D_SetDestCenter(objEnemy); 

   ObjMove_SetDestAtFrame(objEnemy, cx, 130, 60);
   ObjEnemy_SetLife(OBJ_ENEMY_BOSS,3000);
   MAIN;
}

@MainLoop {
   ex = ObjMove_GetX(objEnemy);
   ey = ObjMove_GetY(objEnemy); 
   ObjEnemy_SetIntersectionCircleToShot(objEnemy, ex, ey, 32);
   ObjEnemy_SetIntersectionCircleToPlayer(objEnemy, ex, ey, 24);
   frame++;
   yield;
   if(ObjEnemy_GetInfo(objEnemy, INFO_LIFE) <= 0) { Obj_Delete(objEnemy); CloseScript(GetOwnScriptID()); return; }
 }

@Finalize {  }

task MAIN {
   let count = 0;
   wait(120);
   let a_rand = 40;
   loop {
      fuji(cx+70, a_rand);
      fuji(cx-70, a_rand);
      fuji(cx+130, a_rand);
      fuji(cx-130, a_rand);
      fuji(cx+190, a_rand);
      fuji(cx-190, a_rand);
      wait(400);
      IRON(count%2);
      wait(400);
      count++;
      a_rand+=40;
   }
}

task fuji(X,a_rand) {
   let Y = bottom; let r = 0; let a = rand(a_rand,-a_rand);
   let time = 560;
   while(Y>-15) {
      fujishot(X-r*sin(a),Y,atan2(8,-(r-1)*sin(a-11)+r*sin(a)),time);
      r+=1;  a+=11; Y-=8; wait(4);
      time-=7;
   }
}

task fujishot(x,y,f_a,time) {
   let obj = CreateShotA1(x, y, 0, f_a, DS_RICE_S_GREEN, 15);
   wait(120);
   let obj2 = CreateShotA2(x,y,0,f_a,0.02,2,DS_RICE_S_RED,8);
   Obj_Delete(obj);
   wait(time);
   ObjShot_FadeDelete(obj2);
}

task IRON(i) {
   if(i==0) {
      loop(30) {
         circle_1;
         wait(10);
      }
   }
   else {
      loop(20) {
         circle_2;
         wait(15);
      }
   }
}

function circle_2 {
   let fujiX = rand(0,right);
   let fujiA = atan2(GetPlayerY-(-30), GetPlayerX-fujiX); 
   CreateShotA1(fujiX,-30, 4, fujiA, DS_BALL_M_RED,0);
   ascent( i in 0..11) {
      CreateShotA1(fujiX+20*cos(i*360/11),-30+20*sin(i*360/11), 4, fujiA, DS_BALL_S_RED,0);
   }

function circle_1 {
   let fujiX = rand(0,right);
   let fujiA = 90+rand(-15,15); //角度
   CreateShotA1(fujiX,-30, 4, fujiA, DS_BALL_M_ORANGE,0);
   ascent( i in 0..11) {
      CreateShotA1(fujiX+20*cos(i*360/11),-30+20*sin(i*360/11), 4, fujiA, DS_BALL_S_ORANGE,0);
   }

function wait(time) {
   loop(time) {yield;}
}

 ||<

※本当はcircleはIRONの中に入れた方がいいとかあるかもしれませんが割愛。よくわかんないし。

no.6 マイクロスレッドと弾オブジェクトの基礎(1)

練習と説明がてらに風神録EXTRA

「諏訪大戦 ~ 土着神話 vs 中央神話」をパクりましょう。

めんどくさいので発狂はさせません。各自させてください。

エフェクトも当然無いです。ボス画像はルーミアで行きましょう。

続きを読む

>>目次:no.0