左クリックを長押ししている間に作動するものを作って、その進行度を進行バーで表示します。進行中はアニメーションさせて音声を鳴らし、終了すると別の音を鳴らします。
まずCubeにコライダーとスクリプト、アニメーター、オーディオソースコンポーネントを追加しました。
新規タグも付けます。
アニメーターコンポーネントはCubeに適当なアニメーションを付けたときに自動で追加されました。アニメーターコントローラーでは、まずEntryからモーションの無いステートに遷移し、その後作動中のモーションの付いたステートと行き来するようにします。
+ボタンからBoolパラメーターを作って、行き来するときの遷移の条件にします。
両方の遷移の矢印でHas Exit Timeのチェックをはずして、Conditionsにパラメーターの条件を設定します。
trueだと作動中へ遷移してfalseで戻ります。
Cubeにつけるスクリプトでこのパラメーターや進行度を管理します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TaskScript : MonoBehaviour
{
public bool isInProgress; // 進行中かどうか
public float Progress { get; private set; } = 0f; // 進行度
public float difficulty = 0.001f; // 進行速度
Animator animator;
AudioSource audioSource;
[SerializeField] AudioClip[] audioClips;
// Start is called before the first frame update
void Start()
{
animator = GetComponent<Animator>();
audioSource = GetComponent<AudioSource>();
audioSource.loop = true;
}
// Update is called once per frame
void Update()
{
// 進行中のとき
if (isInProgress)
{
Progress += difficulty; // 進行度を上げる
// 進行度が1以上になったら停止
if (Progress >= 1f)
{
Progress = 1f;
isInProgress = false;
animator.SetBool("IsStarted", false);
audioSource.loop = false;
audioSource.clip = audioClips[1]; // 停止音
audioSource.Play();
}
else
{
}
}
}
// 作業を開始する
public void StartWorking()
{
if (Progress < 1f)
{
animator.speed = 1f;// animatorSpeed;
isInProgress = true;
animator.SetBool("IsStarted", true);
audioSource.clip = audioClips[0]; // 作動音
audioSource.Play();
}
}
// 作業を停止する
public void StopWorking()
{
isInProgress = false;
animator.speed = 0f; // アニメーションを一時停止
if(Progress < 1f) audioSource.Pause(); // 音声を一時停止
}
}
publicメソッドで作業の開始/停止ができるようにします。進行度が1未満のとき開始すると、パラメーターがtrueになって作動音が再生されます。作動中はUpdate()で進行度が上がっていき、1以上になったら停止します。
停止すると、パラメーターがfalseになって、停止音を再生します。このときに音声がループしないようにします。
// 進行度が1以上になったら停止
if (Progress >= 1f)
{
Progress = 1f;
isInProgress = false;
animator.SetBool("IsStarted", false);
audioSource.loop = false;
audioSource.clip = audioClips[1]; // 停止音
audioSource.Play();
}
メソッドで作業が停止されたときは、アニメーターのスピードを0にすることでアニメーションを一時停止し、音声もStop()でなくPause()を呼びます。
// 作業を停止する
public void StopWorking()
{
isInProgress = false;
animator.speed = 0f; // アニメーションを一時停止
if(Progress < 1f) audioSource.Pause(); // 音声を一時停止
}
進行度が1以上のときも音声を一時停止すると、左クリックで停止音も止まってしまうので、1未満の条件を付けました。
作業の開始/停止
FirstPersonControllerクラスで顔からレイを飛ばして、Cubeに当たっているときに左クリックで作業の開始/停止をします。
int state;
TaskScript sc_task;
[SerializeField] Image bar; // 進行バー
[SerializeField] float armLength = 3f; // 手の長さ
[SerializeField] GameObject hand; // 手の画像
// ---
// Update()
// 何もしていないとき
if (state == 0)
{
RaycastHit hit;
if (Physics.Raycast(m_Camera.transform.position, m_Camera.transform.forward, out hit, 50f,~(1 << 9)))
{
Debug.DrawRay(m_Camera.transform.position, m_Camera.transform.forward * 50f);
GameObject hitObj = hit.collider.gameObject;
string hitTag = hit.collider.tag;
float hitDistance = hit.distance;
// 手が届く時
if (armLength >= hitDistance)
{
if (hitTag == "Task")
{
// 手のマーク表示
hand.SetActive(true);
//作業中へ
if (Input.GetMouseButtonDown(0))
{
// 手のマーク非表示
hand.SetActive(false);
sc_task = hitObj.GetComponent<TaskScript>();
sc_task.StartWorking(); // 作業開始
// 進行バーを表示
bar.gameObject.SetActive(true);
bar.fillAmount = sc_task.Progress;
state = 1;
}
}
// それ以外のタグのとき
else {
// 手のマーク非表示
hand.SetActive(false);
}
}
// 手が届かないとき
else {
// 手のマーク非表示
hand.SetActive(false);
}
}
// レイが当たらないとき
else
{
// 手のマーク非表示
hand.SetActive(false);
}
}
// 作業中
else if (state == 1)
{
// 進行度を進行バーに反映
bar.fillAmount = sc_task.Progress;
// 左クリックを離すと何もしていない状態へ
if (Input.GetMouseButtonUp(0))
{
bar.gameObject.SetActive(false); // 進行バーを非表示
sc_task.StopWorking(); // 作業を停止
sc_task = null;
state = 0;
}
}
頭からのレイが自分の体に当たってしまうので、体にレイヤーを追加して、それを無視するようにレイヤーマスクを設定しました。
~(1 << 9)
インスペクタで設定する手の長さより近くでレイが当たると、タグでCubeを見分けて、手のマークを表示します。左クリックが押されると手のマークを消して、作業開始のメソッドを呼び、進行バーを表示します。
進行バーは前の記事の方法で作って、上のスクリプトで進行度の値を反映しています。左クリックを離すとバーを非表示にして作業停止のメソッドを呼びます。
プレイヤーの状態を整数で管理しています。何もしていないとき0で、作業開始すると1にします。そして、作業を停止するときに0にして何もしていない状態に戻しています。