【Unity2D】エリア移動で仕掛けをリセットする
オブジェクトを押して指定位置に行ったら仕掛けが作動する仕組みを作りました。
エリア移動したら仕組みをもとに戻さないと間違えて動かしたときに取り返しがつかなくなってしまいますので
ユーザビリティ向上のためにはエリア移動によるリセットをしてあげる必要があります。
トリガーとしてはエリアスクロールが完了した後が好ましいですね。
スクロール開始瞬間に修正すると画面移動中にオブジェクトが瞬間移動してしまうのが見えてしまいます。
ゲームとして不自然なところはなるべく見えないところでやるのが定石ですね。
モンスターも例外ではありません。
それではエリアスクロールが終わったらオブジェクトは初期位置に戻るという仕組みを作りましょう。
エリアスクロール完了後のトリガーを作成
Move_Cameraがスクロール処理をしていましたね。
しかしここで色々リセットしてしまうのは名目上よろしくないので、
やはりステートを変化させてそれをトリガーとして動かすのがいいです。
そしてそのトリガーが動いたらゲーム内の動く予定のあるオブジェクトを管理するマネージャーがあると楽です。
現在プレイヤーがいるエリアを保持する変数はすでにスクロールを作ったときに確保済みなので、
あとはエリアスクロールを完了したかどうかを知るためのbool変数があるとよさそうです。
ついでに前にいたエリアも保存しておくと何かの仕掛けに使えそうです。ついでに変数作っておきましょう。
State_Managerクラスに作ってましたが、プレイヤーにしか影響しないのでState_Playerにまとめておきましょう。
// 現在のエリアナンバー
protected int _nowAreaNo = 13;
public int NowAreaNo
{
get
{
return _nowAreaNo;
}
set
{
_nowAreaNo = value;
}
}
// 1つ前にいたエリアの保持変数
protected int _beforeAreaNo = 0;
public int BeforeAreaNo
{
get
{
return _beforeAreaNo;
}
set
{
_beforeAreaNo = value;
}
}
// エリア移動中かどうか
protected bool _isAreaScroll = false;
public bool IsAreaScroll
{
get
{
return _isAreaScroll;
}
set
{
_isAreaScroll = value;
}
}
クラス変数じゃなくてインスタンス変数でいいですね。
シーンが変わったらエリアの構成も変わるので静的じゃなくてもよさそうです。
あとはエリアイベント移動が発動したら、元居たエリアを保持してスクロールフラグをONにします。
スクロールフラグがONになったらオブジェクト位置リセットフラグを立て、
スクロールが終わってスクロールフラグがOFFになったら位置リセットフラグを折ることで位置リセットを行うようにしたいです。
位置リセットまでの仕組みを作る
それでは早速組んでいきましょう。
まずはエリア移動したときの処理ですね。
_ObjectCollisionクラスに追記しましょう。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class _ObjectCollision : MonoBehaviour
{
// プレイヤーステートスクリプトの取得
private State_Player _State;
// Start is called before the first frame update
void Start()
{
_State = GameObject.FindGameObjectsWithTag("State_Manager")[0].GetComponent<State_Player>();
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter2D(Collider2D other)
{
// 移動モード変数
int sceneNo = 0;
if (_State.GameSceneState == 0)
{
// タグにより移動モードを決定
switch (other.tag)
{
case "AreaChangeV":
// 移動前のエリアを保持しておく
_State.BeforeAreaNo = _State.NowAreaNo;
//
// 上下判定
if (transform.position.y > other.transform.position.y)
{
// プレイヤーが上にいる場合は下スクロール
sceneNo = 1;
// エリアを+4する
_State.NowAreaNo += 4;
}
else
{
// プレイヤーが下にいる場合は下スクロール
sceneNo = 2;
// エリアを-4する
_State.NowAreaNo -= 4;
}
// モードのステートを設定
_State.setSceneControl(sceneNo);
// スクロールフラグをONにする
_State.IsAreaScroll = true;
break;
case "AreaChangeH":
// 移動前のエリアを保持しておく
_State.BeforeAreaNo = _State.NowAreaNo;
// 上下判定
if (transform.position.x < other.transform.position.x)
{
// プレイヤーが左にいる場合右はスクロール
sceneNo = 3;
// エリアを+1する
_State.NowAreaNo += 1;
}
else
{
// プレイヤーが右にいる場合左はスクロール
sceneNo = 4;
// エリアを-1する
_State.NowAreaNo -= 1;
}
// モードのステートを設定
_State.setSceneControl(sceneNo);
// スクロールフラグをONにする
_State.IsAreaScroll = true;
break;
default:
break;
}
}
}
private void OnTriggerExit2D(Collider2D other)
{
// エリアスクロール中は判定から抜けたらステートを強制0にする
if (_State.GameSceneState >= 1 || _State.GameSceneState <= 4)
{
_State.ActState = 0;
}
}
}
一旦全部載せました。
スクロールが確定したらスクロールフラグをONにします。
スクロール前に現在エリアの保持を忘れないようにしましょう。
次に_ObjectStateGeneralを改良します。
using System.Collections;
using UnityEngine;
public class _ObjectStateGeneral : MonoBehaviour
{
// 押しオブジェクトの移動状態
public bool isMoved_pushObj = false;
// オブジェクトの初期位置
public Vector3 initPos;
// 動かす正解の位置
public Vector3 truePos;
private State_Player _State;
private bool _posInitFlag = false;
void Start()
{
_State = GameObject.FindGameObjectsWithTag("State_Manager")[0].GetComponent<State_Player>();
}
void Update()
{
// 位置リセットフラグチェック
_posInitFlagCheck();
// 位置リセット
_posInit();
}
private void _posInitFlagCheck()
{
if (_State.IsAreaScroll && !_posInitFlag)
{
_posInitFlag = true;
}
}
private void _posInit()
{
if (!_State.IsAreaScroll && _posInitFlag)
{
_posInitFlag = false;
transform.localPosition = initPos;
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.name == "hammer" && this.tag == "brokenBlock")
{
this.GetComponent<SpriteRenderer>().color = new Color(0, 0, 0, 0);
transform.position = Vector3.zero;
}
}
}
個々のオブジェクトでプレイヤーステートを取得することになるので、
処理的にちょっと懸念があるのですが一旦はこのままでいきましょう。
おそらく今のPCであればそこまで気にしないでいい処理負荷だと思います。
シーン中にこれをアタッチするオブジェクトが100個になることもないのでおそらくは大丈夫(笑)
そして位置リセットフラグと位置リセットフラグ切り替え監視関数、そして位置リセット関数を作りました。
次にカメラスクロール側でスクロールが終わったらスクロールフラグを解除しましょう。
Move_Cameraを編集します。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move_Camera : Move_Manager
{
// カメラオブジェクト
private GameObject _MainCamera;
// プレイヤーステートスクリプトの取得
private State_Player _State;
private float _ScrollSpeed = 30;
// 縦スクロール量
private int _vScroll = 192;
private int _vDefaultScroll = 124;
// 横スクロール量
private int _hScroll = 291;
private int _hDefaultScroll = 514;
// 更新処理
protected override void Start()
{
// プレイヤーオブジェクトの取得
_MainCamera = GameObject.FindGameObjectsWithTag("MainCamera")[0];
_State = GameObject.FindGameObjectsWithTag("State_Manager")[0].GetComponent<State_Player>();
}
protected override void Update()
{
_areaScroll(_State.GameSceneState);
}
// ステートが1-4だとスクロールする
private void _areaScroll(int scrollNo)
{
switch (scrollNo)
{
case 1:
// カメラオブジェクトを移動させる
_MainCamera.transform.Translate(0, -_ScrollSpeed / 10, 0);
// _vScrollで割り切れたらスクロール停止(切り捨て
if ((Math.Floor(_MainCamera.transform.position.y) - _vDefaultScroll) % _vScroll == 0)
{
_State.setSceneControl(0);
// 現在エリアを元に位置補正する
_MainCamera.transform.position = new Vector3(_State.TrialMapVector2[_State.NowAreaNo].x, _State.TrialMapVector2[_State.NowAreaNo].y, -10);
}
// スクロールフラグを解除
_State.IsAreaScroll = false;
break;
case 2:
_MainCamera.transform.Translate(0, +_ScrollSpeed / 10, 0);
// _vScrollで割り切れたらスクロール停止(切り上げ
if ((Math.Ceiling(_MainCamera.transform.position.y) - _vDefaultScroll) % _vScroll == 0)
{
Debug.Log(Math.Ceiling(_MainCamera.transform.position.y));
_State.setSceneControl(0);
// 現在エリアを元に位置補正する
_MainCamera.transform.position = new Vector3(_State.TrialMapVector2[_State.NowAreaNo].x, _State.TrialMapVector2[_State.NowAreaNo].y, -10);
// スクロールフラグを解除
_State.IsAreaScroll = false;
}
break;
case 3:
// カメラオブジェクトを移動させる
_MainCamera.transform.Translate(_ScrollSpeed / 10, 0, 0);
// _vScrollで割り切れたらスクロール停止(切り捨て
if ((Math.Ceiling(_MainCamera.transform.position.x) - _hDefaultScroll) % _hScroll == 0)
{
_State.setSceneControl(0);
// 現在エリアを元に位置補正する
_MainCamera.transform.position = new Vector3(_State.TrialMapVector2[_State.NowAreaNo].x, _State.TrialMapVector2[_State.NowAreaNo].y, -10);
// スクロールフラグを解除
_State.IsAreaScroll = false;
}
break;
case 4:
_MainCamera.transform.Translate(-_ScrollSpeed / 10, 0, 0);
// _vScrollで割り切れたらスクロール停止(切り上げ
if ((Math.Floor(_MainCamera.transform.position.x) - _hDefaultScroll) % _hScroll == 0)
{
_State.setSceneControl(0);
// 現在エリアを元に位置補正する
_MainCamera.transform.position = new Vector3(_State.TrialMapVector2[_State.NowAreaNo].x, _State.TrialMapVector2[_State.NowAreaNo].y, -10);
// スクロールフラグを解除
_State.IsAreaScroll = false;
}
break;
}
}
}
これでいい感じにエリアが切り替わって動かしたオブジェクトの位置が戻ります。
一旦ここまで通して動画をとってみました。
#Unity pic.twitter.com/GHtt06QXeH
— なおキーヌ@ゲームクリエイターLv5 (@naokeyzmt) January 9, 2020
うん、いい感じですね!
色々動かしてたらおかしな挙動もちょろちょろでてきてますが、今のところ想定の動作をしてくれています。
いやぁ、10年前の私にここまでできるんだってことを見せてやりたいですね。
次回は仕掛けを解いたら開く扉を作りましょう。
それでは!