見出し画像

Unity1Week「かえす」に参加しました。

Unity1Week「かえす」に参加しました。

今回も遅刻投稿となってしまいました。


まず蛇足から

C105、楽しかったです。久しぶりに行ったのですが相変わらず暑い。そして柄にもなくエンジョイしすぎて翌日ド派手に体調を崩した……というか何かしら感染症っぽいのを発症しました。そういうわけでUnity1Weekにちゃんと参加できなかった、というわけです。

クラス図を書いた(事後)

今回もクラス図に挑戦してみますが既に遅刻確定でしたので投稿が終わってから作りました。


なかなかひどいクラス図ではないでしょうか。Ballクラスは一体何個から参照され何個参照しているのでしょうか。GameConfigクラスは一体何者なのでしょうか。スタート画面からゲーム本編に遷移するときにプレイヤーから何mで遊ぶかの指定を StartMenu から GameMessenger に載せて DontDestroyOnLoad で伝達する予定だったですが GameConfigクラスいらなくない……?

前回(下の記事)は一応事前に設計をしたのですが、そのときの方が断然キレイだった気がします。

あと今のところ他人のブログを頼りに勘で書いているのでクラス図の書き方をちゃんと学びたいです。

斜方投射

今回のゲーム内ではキャッチボールをするためCPUからプレイヤーへのボールの斜方投射が必要です。今回のケースでは、プレイヤーまでの距離、高さ、射出角度が分かっている状態で一体速度をどのようにすれば届くか、という場面です。式を自分で書き出すのは面倒なのでChatGPTに書かせました。本当に便利です。
初速度を求める式は

$$
v_0 = \sqrt{\frac{g d^2}{2 \cos^2\theta \left( d \tan\theta + y_0 - y \right)}}
$$

となるらしいです。以下がUnityで計算したときのコードです。

void ThrowBall(){
		//ボールを生成
		var obj = Instantiate(ball, fromTransform.position, fromTransform.rotation);
		
		//ボールにプレイヤーかCPUか伝達
		obj.GetComponent<Ball>().IsPlayer = false;
		var rb = obj.GetComponent<Rigidbody>();
		
		//target(=プレイヤー)とfromTransform(=CPUの腕辺りの位置の空オブジェクト)の差を取得
		Vector3 direction=target.position-fromTransform.position;
		
		//高さの差を取得
		float heightDifference=direction.y;
		
		//平面上での距離の測定のためyをゼロに
		direction.y=0;
		
		//平面上での距離取得
		float distance=direction.magnitude;
		
		//射出角度を計算、ここの式は勘で
		float angle=30f+distance*0.25f;
		float radAngle=angle*Mathf.Deg2Rad;
		
		float gravity=Mathf.Abs(Physics.gravity.y);
		
		float cosAngle=Mathf.Cos(radAngle);
		if(cosAngle==0f){
			return;
		}
		float tanAngle=Mathf.Tan(radAngle);
		
		//初速度計算
		float initialSpeed=Mathf.Sqrt(distance*distance*gravity/(2*cosAngle*cosAngle*(distance*tanAngle-heightDifference)));
		
		//速度ベクトルを計算
		Vector3 velocity=initialSpeed*cosAngle*direction.normalized;
		velocity.y=initialSpeed*tanAngle*cosAngle;
		
		//ボールに力を加える
		rb.velocity=velocity;
    }

今となっては斜方投射の速度計算部分だけ別メソッドにすればよかったですね。

どっちがいいの?イベントリスナーの追加

今回新しく取り組んだこととしてUIのイベントリスナーをスクリプトから追加することがあります。今まではスクリプトでpublicメソッドを書いてエディタ上でポチポチ登録していましたが、スクリプトで追加できることを知りました。メソッドを記載するスクリプトと同一スクリプト内に処理が書けるのでスクリプトから追加した方が管理が楽な気がします。以下が実際にスタート画面で使用したスクリプトです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class StartMenu : MonoBehaviour
{
	[SerializeField] Slider slider;
	[SerializeField] Text text;
	[SerializeField] Button button;
	[SerializeField] GameMessenger gm;
	const string sceneName="SampleScene";
    // Start is called before the first frame update
    void Start()
    {
		text.text=slider.value+" m";
        
		slider.onValueChanged.AddListener((x)=>{
			text.text=x+" m";
		});
		
		button.onClick.AddListener(()=>{
			Instantiate(gm).GetComponent<GameMessenger>().SetGameConfig(new GameConfig(slider.value));
			SEManager.instance.PlaySEClip(0);
			SceneLoader.instance.LoadScene(sceneName);
		});
    }
}

このようにラムダ式で処理を渡せるのでスクリプトを遡る必要もなくかなり楽な印象です。
しかしスクリプトからイベントを追加しようとしたらUIを格納しておくためのSerializeFieldが生まれました。今回はボタンとスライダーが一個ずつでしたが増えたら大変なことになりそうな予感がします。これは設計的にはいいことなのでしょうか……。

最後に

今回のゲームはただキャッチボールをするだけですが、実は最初は飛んできたボールをホームまで返球するゲームにしようとしていました。もう時間が無いので色々要素を削りまくった結果こうなってしまいました。ただその割には意外といい出来だと思っております。ということで是非一度遊んでみてください!

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