見出し画像

Unity1Week「2」に参加した話

今更ですが、まとめっぽいものをここに残します。今回は珍しくアクション系を作ってしまったのでそこら辺の話を少々(そこそこ多いかも)。

まず、何を作ろうかと

今回は珍しく、1日目で方針が決まりました。進歩しましたね!

そのとき思いついたゲーム案がこちら。

無題2_20210426222522

あからさまな「2」の要素、2ってぱっと見てフックみたいに見えませんか?ということでこれでスタートしました。

(さて、1Week開催のちょっと前に発売されましたねぇ! 9-nine-が! 私、買いまして…面白かったですね~。ということで初めのうちはほとんど作業をしませんでした。あとで地獄を見ます。)

ワイヤーアクションっぽいヤツのスクリプト

めちゃくちゃ面倒でした、ワイヤーアクション。

ということで忘れないようにここに色々書き残します。

今回は時間も無く忙しかったので(自業自得)1ファイルにごちゃごちゃとスパゲッティコード状態。スクリプトをドーンしたいけどまず少し説明を…。

ワイヤーアクションは、

マウスの位置を取得→発射→アンカーにヒット→キャラを引き寄せ

という順序で行っています。

なので、スクリプトもこの順で説明を残します。グダグダわかりにくい文章ですけどね。

まず変数たちを

#region ワイヤーアクション系の変数
Transform shotRay; //赤い線のtransform
LineRenderer shotRayLr; //赤い線
Vector3[] rayPos=new Vector3[2]; //赤い線の始点、終点
Vector3[] chainPos=new Vector3[2];
Vector3 rayDire=Vector3.zero; //レイの方向(大きさ:1)
bool nowWire=false; //ワイヤーアクションをしているか
bool coming=false; //ワイヤーに引き寄せられているか
RaycastHit2D hit; //レイが当たったオブジェクト
int layermask=1<<9; //フィールドにしか当てたくない
float rayLength=0f; //レイの長さ
float chainCoolTime=0f; //連続でやられたら敵わん
	
Quaternion shotAngle; //チェーンの回転
Transform shootChain; //チェーンを撃つtransform
LineRenderer chainLr; //チェーン
Rigidbody2D rbChain; //チェーンの物理
Vector2 chainVelocity=Vector2.zero; //チェーンの速度
	
Transform pivotObj; //チェーンの初期回転用
	
bool onForward=false,onBack=false; //後ろと正面確認
	
public GameObject bullet,chain;
#endregion

まず、変数定義系を。すでにすごく多い。それにガバガバ英語とローマ字系変数が多いけれども、あしからず…。当時の自分にとっては読みやすかったんです。

マウス関連のスクリプト

void LookMousePos(){
	if(nowWire==true){
		return;
	}
		
	rayPos[0]=shotRay.position;
	rayPos[1]=mainCamera.ScreenToWorldPoint(Input.mousePosition);
	rayPos[1].z=0f;
		
	shotRay.LookAt(rayPos[1]);
	rayDire=(rayPos[1]-rayPos[0]).normalized;
		
	hit=Physics2D.Raycast(shotRay.position,shotRay.forward,15f*2f,layermask);
		
	if(hit.collider!=null){
		rayLength=hit.distance;
	}else{
		rayLength=15f;
	}
		
	rayPos[1]=rayPos[0]+rayDire*rayLength;
	shotRayLr.SetPositions(rayPos);
}

nowWireは、ワイヤーアクション中はtrueになるもので、2重に発動するのを防いでいます。

rayPosには自身の座標とマウスの座標を入れています。ここでマウスの方はZ座標の補正も入れておきます。

また、あとで使うので自身をマウスの方向に向け、自身とマウス間の単位ベクトルも取得しておきましょう。

その後、レイを撃ちアンカーオブジェクトにヒットしたら、その距離をrayLengthに入れておき、rayPos[1]をヒットした座標に置き換えておきましょう。

そのあとに書いてあるshotRayLr~~~のところはラインレンダラーなので今はスルーします。

いざ、ワイヤー発射

void ShotChain(){
	if(chainCoolTime>0f){
		chainCoolTime-=deltaFrame;
	}
	
	if(Input.GetMouseButtonDown(1)){
		if(chainCoolTime<=0f){
			StartCoroutine(ExtendChain());
		}
	}
}
	
IEnumerator ExtendChain(){
	if(nowWire==true){
		yield break;
	}else{
		rb.velocity=Vector2.zero;
		nowWire=true;
	}
	
	chainPos[0]=my.position;
	chainPos[1]=chainPos[0];
	float _dis=0f;
	
	while(_dis<rayLength){
		chainPos[1]+=rayDire;
		_dis++;
		if(_dis%5f==1f){
			bm.PlaySEClip(1);
		}
		chainLr.SetPositions(chainPos);
		yield return null;
	}
	chainPos[1]=my.position+rayDire*rayLength;
	chainLr.SetPositions(chainPos);
	
	if(hit.collider!=null){
		shotAngle.SetFromToRotation(Vector3.up,rayDire);
		
		chainPos[1]=hit.transform.position;
		chainPos[1].y-=0.9f;
		chainPos[1].x+=0.1f;
		
		pivotObj.position=chainPos[1]+Vector3.up*0.8f;
		
		shootChain=Instantiate(chain,chainPos[1],Quaternion.identity).transform;
		
		shootChain.SetParent(pivotObj);
		pivotObj.rotation=shotAngle;
		
		shootChain.parent=null;
		
		rbChain=shootChain.GetComponent<Rigidbody2D>();
		StartCoroutine(FirstWireAction());
	}else{
		chainPos[1]=chainPos[0];
		chainLr.SetPositions(chainPos);
		nowWire=false;
	}
}

まず頭から、ShotChainでワイヤー発射の管理をします。クールダウン中には発射できません。

ワイヤーの伸びるアニメーションはコルーチンを使っています。ExtendChainですね。多分英語あってますよね???

関数の一番頭のところは(個人的に)よくやるやつです。重複処理されたら困りますもんね。 else内で一度速度をゼロにしていますが、あれは操作キャラの速度で、ワイヤーアクションを落下などで邪魔されたくないのでゼロにしています。

chainPosをワイヤーアクションの始点、終点を入れる変数にしてあります。

直後のwhile内では、変数がrayLengthになるまで、つまりヒットした座標になるまで単位量ずつ増加させます。(その間、ラインレンダラーでワイヤー感を演出します。)

伸びきったら、引き寄せを開始します。

まず、ヒットしたときの角度を取得します。

その後、(色々あったので)座標を補正して、ワイヤーアクション時に軸となるオブジェクト(pivotObj)を設置します。

そして、ワイヤーの先端を生成し、pivotObjにペアレント。

あとで使うのでチェーンのオブジェクトのRigidbodyを取得しておきましょう。rbChainはそのための変数です。

次に行うキャラの移動は、別のコルーチン(FirstWireAction)に任せます()

キャラを引き寄せる

IEnumerator FirstWireAction(){
	if(hit.collider==null){
		yield break;
	}
	
	rayPos[0]=Vector3.zero;
	rayPos[1]=rayPos[0];
	shotRayLr.SetPositions(rayPos);
	
	coming=true; //引き寄せ開始
	
	myParent=shootChain;
	
	int lengthInteger=Mathf.FloorToInt(rayLength);
	
	for(int i=0;i<lengthInteger;i++){
		my.position+=rayDire;
		chainPos[0]=my.position;
		if(i%5==1){
			bm.PlaySEClip(1);
		}
		chainLr.SetPositions(chainPos);
		yield return null;
	}
	
	y=-1f;
	my.position=myParent.position+myParent.up*y;
	
	chainPos[0]=myParent.position;
	chainPos[1]=chainPos[0];
	chainLr.SetPositions(chainPos);
	coming=false; //引き寄せ終了
}

さて、だいぶ前に使ったrayPosはもう使わないのでゼロを代入しておき、ラインも消しておきましょう。

さて、for文内でまたも単位量ごとに動かしたいので、基本は整数でアクションさせます。始めに取得したrayDireをループ内で代入し、最後に少数部分を代入するとまるで引き寄せられたかのようなアクションの出来上がり。

最後はアンカーの少し下部分にキャラを移動させて、終わりです。

いや~、長かった。

その他ワイヤーアクション関連のこと

とりあえず、ワイヤーを伸ばして、引っ掛けて、飛びつくところまでの説明は書き終わったので、あとはその後。つまりUpdate内での処理の説明です。

ワイヤーを伸び縮みさせたり、揺らした反動で飛び上がったりと色々アクションはまだありますので、それらを雑に紹介。もう一番書きたかったことは前の部分でだいぶ書けたのでOKですね。

void Swing(){
	x=0f;
	if(Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D)){
		x+=speed;
	}
	
	if(Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A)){
		x-=speed;
	}
	
	if(Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W)){
		y+=deltaFrame*speed;
	}
	
	if(Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S)){
		y-=deltaFrame*speed;
	}
}

IEnumerator ChainRecoil(float _power){
	float num=1f;
	y=Mathf.Abs(_power)*10f;
	for(int i=0;i<15;i++){
		my.position+=Vector3.right*_power*deltaFrame*speed*5f*num;
		num*=0.75f;
		yield return null;
	}
}

void RemoveChain(){
	nowWire=false;
	chainPos[0]=Vector3.zero;
	chainPos[1]=chainPos[0];
	chainLr.SetPositions(chainPos);
	Destroy(shootChain.gameObject);
	my.rotation=Quaternion.identity;
}

ここら辺がそのスクリプト。

Swing内でx軸の速度とy軸…というかワイヤーを伸び縮み具合を変化させることができます。

ChainRecoilではワイヤーを離したときのキャラの反動のためのスクリプトです。コルーチンで座標をずらして、まるで物理で移動しているかの如く。

RemoveChainは離れたあとのワイヤーの処理です。これが呼ばれたらワイヤーアクションは終了です。

//Update内での処理

if(nowWire==true){
	//ワイヤーアクション中に
	if(myParent!=null){
		if(coming==false){
			//もう引き寄せアニメーションが終わってたら
			Swing();
			my.position=myParent.position+myParent.up*y;
			chainPos[0]=my.position;
			chainPos[1]=myParent.position;
			chainLr.SetPositions(chainPos);
			if(front==1){
				my.rotation=myParent.rotation;
			}else{
				my.rotation=myParent.rotation*backAngle;
			}
			rbChain.velocity+=Vector2.right*x*deltaFrame;
			
			if(Input.GetMouseButton(1)){
				bm.PlaySEClip(0);
				chainCoolTime=0.25f;
				StartCoroutine(ChainRecoil(rbChain.velocity.x));
				RemoveChain();
			}
			
			if(onBack==true || onForward==true){
				rbChain.velocity=Vector2.zero;
				RemoveChain();
			}
			
			if(onGround==true){
				RemoveChain();
			}
		}
	}
}

そして前のスクリプト群で書いたことを順番に行っていきます。

Swing内で左右にワイヤーを揺らす力を加え、ワイヤーの先端と自身の間にチェーン用のラインを引く。front(左右のどちらを向いているかを示す変数)に応じてプレイヤーの向きを変えます。

チェーンに力を加えてスイング、もしクリックされたらワイヤーを外して、ChainRecoilの飛ぶアクションへ移ります。このとき引数にrbChainのx軸の速度を入れます。その後RemoveChainを呼び出します。

また、ワイヤーを自ら切ったときはもちろん、壁や地面に当たったときも先ほどと同じようなアクションをしますが、飛ぶアクションはありません。普通に速度をゼロにして、RemoveChainを呼び出すだけです。

いや~、長かった…。でもこれでおわr…。ん???

今回たしかヒンジジョイント使ったんだったな…。飛ばしているけどワイヤーアクションでかなり重要な役割持っているし、それも書かないとダメじゃね?ということでそちらも書いていきます。

ヒンジジョイントが一番大事な気がする

ヒンジジョイントって何?って人(このゲーム作る前の自分)向けに公式のリンクを…。

3D、2Dのをここに貼り付け。

まず、こんな感じに付けました。

画像2

画像3

設定もだいたい初期設定のまま。引っかかる部分に回転の中心が来るようにして、回転角度に制限を付けたぐらいです。もちろんRigidbody2Dもアタッチされています。

このジョイントは力を加えるとユラユラとまるでぶら下がっているかのように動いてくれます。設定さえ組めればあとはとくに気にすることなく使えるでしょう。

最後に

一応ゲームのリンクを貼っておきますね。ぜひ一度遊んでみてください。

今回の記事、凄く久しぶりに書いたせいか、もとからそういう文章しか書けないのか、とても読みにくい&わかりにくい。まあ、いいでしょう。

今後も1Weekのあとに記事を書くかもしれないので、そのときはぜひ読んでみてください。

とりあえず今回の反省として、真面目制作モードなのに他のゲームに手を出すな、ってことだろうな…。

この記事が気に入ったらサポートをしてみませんか?