はじめに
前に作った反射するレーザーの続き。
前の記事はこちら
https://qiita.com/TanakaNeko/items/fbd57b3c24b7fd4adb0d
今回は、キー操作で下のgifのようにレーザーが回転するようにした。
概要
十字キーの左右でレーザーを回転できる。反射の計算と描画は引き続き、RaycastHit2DとLineRendererを使っている。
作り方
前回作ったスクリプトを以下のものに置き換える。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LazerStarter : MonoBehaviour
{
[SerializeField] private GameObject lazerMother;
private Vector2 INITIAL_DIRECTION = new Vector2(0.3f,1); //最初の方向
const float ROTATE_SPEED = 0.02f; //回転速度
private GameObject newLazer;
private Lazer lazerCS;
public void Start(){
newLazer = Instantiate(lazerMother, this.gameObject.transform.position, Quaternion.identity);
lazerCS = newLazer.GetComponent<Lazer>();
lazerCS.creat(newLazer.transform.position, INITIAL_DIRECTION, 0);
}
public void Update(){
// 左回転
if (Input.GetKey (KeyCode.LeftArrow)) {
lazerCS.move(lazerCS.getOrigin(), Quaternion.Euler(0f, 0f, ROTATE_SPEED) * lazerCS.getDirection());
}
// 右回転
if (Input.GetKey (KeyCode.RightArrow)) {
lazerCS.move(lazerCS.getOrigin(), Quaternion.Euler(0f, 0f, -ROTATE_SPEED) * lazerCS.getDirection());
}
}
}
レーザー発射のメソッドをStart()に変更し、キー操作を受け付けるためにUpdate()メソッドを追加している。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Lazer : MonoBehaviour
{
[SerializeField] private GameObject lazerPrefab;
const float MAX_DISTANCE = 23.0f; //箱の対角線の長さ
const int REFLECT_NUM = 5; //反射回数
private GameObject preWall;
public LineRenderer[] lazers;
public Vector3 nowOrigin;
public Vector2 nowDirection;
/**
* レーザー生成関数
* @param {Vector3} origin - レーザーの原点
* @param {Vector3} direction - レーザーの方向
* @param {int} n - 何本目か(0はじまり)
**/
public void creat(Vector3 origin, Vector2 direction, int n){
if(n == 0){
nowOrigin = origin;
nowDirection = direction;
lazers = new LineRenderer[REFLECT_NUM + 1];
preWall = null;
};
if(n < REFLECT_NUM + 1){
GameObject lazerChild = Instantiate(lazerPrefab, this.transform);
lazerChild.name = "lazer_" + n;
LineRenderer line = lazerChild.GetComponent<LineRenderer>();
lazers[n] = line;
RaycastHit2D[] hits = Physics2D.RaycastAll(origin, direction, MAX_DISTANCE);
foreach(RaycastHit2D hit in hits){
if (hit.collider.gameObject != preWall)
{
Vector3 endPos = hit.point;
Vector2 reflectDirection = Vector2.Reflect(direction, hit.normal);
line.SetPosition(0, origin);
line.SetPosition(1, endPos);
preWall = hit.collider.gameObject;
creat(endPos, reflectDirection, ++n);
break;
}
}
}
}
/**
* レーザー移動時に実行する関数
* @param {Vector3} origin - レーザーの原点
* @param {Vector3} direction - レーザーの方向
**/
public void move(Vector3 origin, Vector2 direction){
preWall = null;
nowOrigin = origin;
nowDirection = direction;
Vector3 startPos = origin;
Vector2 nextDirection = direction;
foreach(LineRenderer line in lazers){
RaycastHit2D[] hits = Physics2D.RaycastAll(startPos, nextDirection, MAX_DISTANCE);
foreach(RaycastHit2D hit in hits){
if (hit.collider.gameObject != preWall)
{
Vector3 endPos = hit.point;
Vector2 reflectDirection = Vector2.Reflect(nextDirection, hit.normal);
line.SetPosition(0, startPos);
line.SetPosition(1, endPos);
preWall = hit.collider.gameObject;
startPos = endPos;
nextDirection = reflectDirection;
break;
}
}
}
}
//getter
public Vector3 getOrigin(){
return nowOrigin;
}
public Vector2 getDirection(){
return nowDirection;
}
}
レーザー移動時に実行する関数と現在のレーザーの情報を返すゲッターを追加している。
レーザーの発射方法を変更しているので注意
前回、ボタンを押すと発射するようにしていたが、今回はStart()メソッドに変更。そのため、ボタンではなく任意のGameObjectにアタッチすることで、そのオブジェクトの生成時にレーザーが発射される。
(オブジェクトの座標からレーザーが発射されるのはそのまま)
スクリプトを置き換えたら、十字キーを押したときにレーザーが動くようになる。
解説
追加した変数
LazerStarter.cs
回転速度の変数と、Update()内でLazer.csの関数を呼び出すための変数を追加。
回転速度は任意の値に変更してOK。
変数名 | 型 | 役割 |
---|---|---|
ROTATE_SPEED | float | キー入力をしたときのレーザーの回転速度 |
newLazer | GameObject | 新たに生成されたLazerMother |
lazerCS | Lazer | 生成したLazerMotherにアタッチされているLazer.cs |
Lazer.cs
レーザーの状態が動的に変化するので、現在のレーザーの情報を入手するための変数を追加。
変数名 | 型 | 役割 |
---|---|---|
lazers | LineRenderer[] | 反射によって生成された各レーザーの持つLineRendererを順に格納する配列 |
nowOrigin | Vector3 | レーザーの現在の始点 |
nowDirection | Vector2 | レーザーの現在の発射方向 |
変数の追加に伴い、いくつかコードを変更した。
newLazer = Instantiate(lazerMother, this.gameObject.transform.position, Quaternion.identity);
lazerCS = newLazer.GetComponent<Lazer>();
lazerCS.creat(newLazer.transform.position, INITIAL_DIRECTION, 0);
newLazerをローカル変数からグローバル変数に変更し、LazerCSに値をいれる行を追加している。この二つをグローバル変数にすることでUpdate()内でも、これらの値を使用できる。
if(n == 0){
nowOrigin = origin;
nowDirection = direction;
lazers = new LineRenderer[REFLECT_NUM + 1];
preWall = null;
};
preWallを初期化していたif文内に、新しい変数の初期化の処理を追加。
lazers[n] = line;
LineRendererを配列に格納する処理を追加。
追加した関数
Update()
public void Update(){
// 左回転
if (Input.GetKey (KeyCode.LeftArrow)) {
lazerCS.move(lazerCS.getOrigin(), Quaternion.Euler(0f, 0f, ROTATE_SPEED) * lazerCS.getDirection());
}
// 右回転
if (Input.GetKey (KeyCode.RightArrow)) {
lazerCS.move(lazerCS.getOrigin(), Quaternion.Euler(0f, 0f, -ROTATE_SPEED) * lazerCS.getDirection());
}
}
キー入力を受け付け、右矢印、左矢印が押されたときに、Lazer.csの関数move()を実行する。
move()の引数には現在のレーザーの原点の座標のVector3と、現在のレーザーの発射方向からROTATE_SPEEDだけ回転させたVector2を渡している。
回転の計算はQuaternion * Vector2で計算している。(よくはわかってはいないが、これで、Quaternionで指定した角度分回転させたベクトルを得ることができる)Vector2 * Quaternionの順では計算できないので注意が必要。
getter
//getter
public Vector3 getOrigin(){
return nowOrigin;
}
public Vector2 getDirection(){
return nowDirection;
}
LazerStarter.csからレーザーの情報を得るために使用する。
move(Vector3, Vector2)
/**
* レーザー移動時に実行する関数
* @param {Vector3} origin - レーザーの原点
* @param {Vector3} direction - レーザーの方向
**/
public void move(Vector3 origin, Vector2 direction){
preWall = null;
nowOrigin = origin;
nowDirection = direction;
Vector3 startPos = origin;
Vector2 nextDirection = direction;
foreach(LineRenderer line in lazers){
RaycastHit2D[] hits = Physics2D.RaycastAll(startPos, nextDirection, MAX_DISTANCE);
foreach(RaycastHit2D hit in hits){
if (hit.collider.gameObject != preWall)
{
Vector3 endPos = hit.point;
Vector2 reflectDirection = Vector2.Reflect(nextDirection, hit.normal);
line.SetPosition(0, startPos);
line.SetPosition(1, endPos);
preWall = hit.collider.gameObject;
startPos = endPos;
nextDirection = reflectDirection;
break;
}
}
}
}
最初のレーザーの始点と方向を引数で受け取り、その後のレーザーの始点、終点もforeach内で計算し、配列lazersに格納されたLineRandererの値を更新する。
反射方向や座標の計算方法は、前回の記事で作ったcreat()メソッド内と同じ。
おわりに
今回はレーザーを動的に動かせるよう変更した。前回作った反射の計算をそのまま使えたので、思っていたより簡単に作ることができた。今回は発射する方向のみ動かせるようにしたが、発射する位置を動かせるようにしても面白そう。
続き
参考