材料
説明する
1つのシーンに対してQFを使用し、UIRoot配下のプレハブを切り替えることでパネル切り替えを実現できます。
ただし、当然ながらテスト中にはテストパネルに直接ジャンプするテストスクリプトが必要で、テストシーンは予約されているので(そうしないと初心者はテストシーンの復元方法が分からない)、全文をシーンごとに分けています。
----------------------------------------------------
01 シーン入り(パネルスクリプト、主に動的(関数)静的(UI参照)分離)
01 まず、UIRoot を作成します (一部は QF にあります)
02 UIをしっかり作り、参照が必要なUIにBindスクリプトを追加します(デフォルト設定は画像参照)
03 ドラッグしてプレハブを作成すると、CreateUICodeによりUI参照用(xxx.Designer)と関数用(xxx)の2つのスクリプトが生成され(自動生成場所は上位ディレクトリのScripts/UI)、関数用はプレハブに自動追加されます
xxxPanelData、xxxPanel (パネルとは呼ばれず、xxxWindow、xxxUI などと呼ばれます)
using UnityEngine;
using UnityEngine.UI;
using QFramework;
using UnityEngine.SceneManagement;
namespace QFramework.Example
{
public class StartGamePanelData : UIPanelData
{
}
public partial class StartGamePanel : UIPanel
{
protected override void OnInit(IUIData uiData = null)
{
mData = uiData as StartGamePanelData ?? new StartGamePanelData();
// please add init code here
Screen.SetResolution(640, 1136, false);//宽,高,不可修改
BtnStart.onClick.AddListener(() => {
Debug.Log("StartGamePanel");
SceneManager.LoadScene("01 SelectLevel");
});
}
protected override void OnOpen(IUIData uiData = null)
{
}
protected override void OnShow()
{
}
protected override void OnHide()
{
}
protected override void OnClose()
{
}
}
}
xxxPanel.デザイナー
using System;
using UnityEngine;
using UnityEngine.UI;
using QFramework;
namespace QFramework.Example
{
// Generate Id:29cfe4a1-ad1e-4350-a490-3bdf8cf34278
public partial class StartGamePanel
{
public const string Name = "StartGamePanel";
[SerializeField]
public UnityEngine.UI.Button BtnStart;
private StartGamePanelData mPrivateData = null;
protected override void ClearUIComponents()
{
BtnStart = null;
mData = null;
}
public StartGamePanelData Data
{
get
{
return mData;
}
}
StartGamePanelData mData
{
get
{
return mPrivateData ?? (mPrivateData = new StartGamePanelData());
}
set
{
mUIData = value;
mPrivateData = value;
}
}
}
}
04 呼び出し、RrsKit.Init(); を呼び出す必要があります
/****************************************************
文件:GameStart.cs
作者:lenovo
邮箱:
日期:2023/7/2 22:59:41
功能:
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
namespace QFramework.Example
{
public class GameStart : MonoBehaviour
{
#region 生命
/// <summary>首次载入</summary>
void Awake()
{
ResKit.Init();
UIKit.OpenPanel<StartGamePanel>();
GameObject.DontDestroyOnLoad(gameObject);
}
#endregion
}
}
06 パッケージ名を書いてマークし(ResKitのみパッケージリストにあります)、ABパッケージを作成します(QFにはパッケージ化なしでは実行できない関連サンプルがあります。編集されていますが、実際にはそれほど高速ではありません)
06 エフェクト
--------------------------------------------------------------
02 シーンセレクション
世界の選択
レベルの選択
スタープラグインフォルダー名
Unity では、Plugins フォルダーの下で、ファーストパス アセンブリに変わります。
--------------------------------------------------------
03 シーンゲームインターフェース
分割列挙型を変更する
初心者にも嬉しいポイント。しかし実際には、Unity の組み込みスクリプトにはクラス内に列挙型が記述されています。
GameUI は Pass パネルと Fail パネルに分割されています
パスパネル
using UnityEngine;
using UnityEngine.UI;
using QFramework;
using UnityEngine.SceneManagement;
namespace QFramework.Example
{
public class SuccPanelData : UIPanelData
{
}
public partial class SuccPanel : UIPanel
{
protected override void OnInit(IUIData uiData = null)
{
mData = uiData as SuccPanelData ?? new SuccPanelData();
// please add init code here
BtnNext.onClick.AddListener(() => {
GameData.LevelIndex++;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
});
}
protected override void OnOpen(IUIData uiData = null)
{
}
protected override void OnShow()
{
}
protected override void OnHide()
{
}
protected override void OnClose()
{
}
}
}
失敗パネル
using UnityEngine;
using UnityEngine.UI;
using QFramework;
using UnityEngine.SceneManagement;
namespace QFramework.Example
{
public class GameOverPanelData : UIPanelData
{
}
public partial class GameOverPanel : UIPanel
{
protected override void OnInit(IUIData uiData = null)
{
mData = uiData as GameOverPanelData ?? new GameOverPanelData();
// please add init code here
BtnReset.onClick.AddListener(()=>{
GameManager.Instance.StartBack();
CloseSelf();
});
BtnReplay.onClick.AddListener(() => {
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
});
BtnHome.onClick.AddListener(() => {
UIKit.OpenPanel<StartGamePanel>();
CloseSelf();
});
}
protected override void OnOpen(IUIData uiData = null)
{
}
protected override void OnShow()
{
}
protected override void OnHide()
{
}
protected override void OnClose()
{
}
}
}
監視 QF でリソースを取得する
new ResLoader() は
爆発エフェクトボール
の廃止されたプレハブです
//在扫雷案例中测试的,用到WhiteChess
ResKit.Init();
ResLoader loader = ResLoader.Allocate();
GameObject prefab = loader.LoadSync<GameObject>("WhiteChess");
Instantiate(prefab , transform);
GameManager を GamePanel に処理する
01 まず、子ノードのすべてのスクリプトを親ノード GamePanel に参照します。
02 スクリプト内で参照されているノードを抽出し、名前を変更し (ノード名と同じ)、Bind スクリプトを追加します。見やすいようにスクリプトの先頭に移動
03 UIを使用するためSpriteRendererをImageに変更
時計「Awake」の特徴
スクリプトでは Awake メソッドのみが使用されており、その前にチェック オプションはありません。
バグ AB リソースが存在しません
はい、もう一度お試しください
バグ 3D を UGUI に変換
SpriteRenderer が画像を転送した後 (パネルを呼び出す QF のメソッドを使用したい)、ボールはほとんど動きません。
このメソッドは、マップ ファイル (UGUI) を生成する前に、元の世界座標の代わりに同じ UUIRoot を使用します。この時点で、親ノードの Scale とプレハブ ボール Ball の RectTranfrom を 1 に調整するように注意してください。そうしないと、ボール間の距離に問題が発生します (問題は、ボール間の距離に問題があるか、トラックが一致していないことです)。
UGUI イメージを変更する
3000 は、カーブに沿って移動するボールの滑らかさです
。0.3 は、ボールの直径にも相当します。この直径は、インスタンス化するボール プレハブの直径です。
このメソッドは、マップ ファイル (UGUI) を生成する前に、元の世界座標の代わりに同じ UUIRoot を使用します。同時に倍率を掛ける必要があります(230が適切です)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapConfig : ScriptableObject
{
public float EndPoint {
get; private set; }
public List<Vector3> pathPointList = new List<Vector3>();
public void InitMapConfig()
{
EndPoint = pathPointList.Count - 2;
}
public Vector3 GetPosition(float progress)
{
Camera ui=Camera.main.gameObject.FindComponentWithTag<Camera>("UI");
int index = Mathf.FloorToInt(progress);
//return Vector3.Lerp(pathPointList[index], pathPointList[index + 1], progress - index);
Vector3 v1 = Vector3.Lerp(pathPointList[index], pathPointList[index + 1], progress - index);
return v1*230f;
}
}
エフェクトトラックボール
-------------------------------------
修正 打球の打球位置、速度、終点が範囲外です
ボールが打ち出される場所
スピード
限界を超えた判断力
キャンバス上のボールをキャンバスの境界までドラッグし、座標を確認するだけです
効果
-------------------------------------------
バグ破壊エフェクトの大きさと位置
using QFramework;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ResLoader = QFramework.ResLoader;
public class FXManager :MonoSingleton<FXManager>
{
Transform FXs;
//
GameObject destroyFXPrefab;
ObjectPool<GameObject> destroyFXPool;
public void Init(Transform FXs)
{
this.FXs = FXs;
//
ResKit.Init();
QFramework.ResLoader loader = QFramework.ResLoader.Allocate();
destroyFXPrefab = loader.LoadSync<GameObject>("DestroyFX");
destroyFXPool = new ObjectPool<GameObject>(InstantiateFX, 10);
}
private GameObject InstantiateFX()
{
GameObject go = Instantiate(destroyFXPrefab, FXs);
go.Hide();
return go;
}
public void ShowDestroyFX(Vector3 pos)
{
GameObject go = destroyFXPool.GetObject();
go.Show();
go.transform.localPosition = pos;
//延时0.5f执行回收操作
ScheduleOnce.Start(this, () =>
{
go.Hide();
destroyFXPool.AddObject(go);
}, 0.5f);
}
}
バグエミッターは 1 回転し、左下に固定されます
これは後で自動的に行われます。おそらく、GamePanel を開くためのループ呼び出しが変更され、複数の GamePanel が作成されたのでしょうか?
バグの自動更新
自動更新がないため、数回クリックしても応答がありません
バグ 3 つの mapConfig は Resources に配置できず、生成されると空白になります。
Resources を置かないと、
GameManager.Instance.mapConfig は実行後に空になります。これは、
パブリック クラス GameSceneConfig:MonoSingleton の MapconfigArr の一部が空であるためです。
打ち上げを持ち上げるとき、バグは敏感ではありません
これを閉じてください
ボールを排除した後も時計が後退しない
スモールボール後は撤退する必要があり、
スモールボール後もボールを排除し続ける状況が続く。
バグが難しすぎて復活後に戻ることができない
SoundManagerを変更する
この例を読んだ後は、UIkit の ResKit.Init() AudioKit.PlaySound("resources://Sound/"+clipName);
AudioKit.PlayMusic("resources://Sound/"+name, volume:volume); のような初期化は必要ありません。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using QFramework;
public class SoundManager : MonoSingleton<SoundManager>
{
static AudioSource bgAudio;
public void Init()
{
bgAudio = gameObject.GetOrAddComponent<AudioSource>();
}
private static void PlaySound(string clipName)
{
// AudioSource.PlayClipAtPoint(GetAudioClip(clipName), Vector3.zero);
AudioKit.PlaySound("resources://Sound/"+clipName );
}
public static AudioClip GetAudioClip(string clipName)
{
return Resources.Load("Sound/" + clipName, typeof(AudioClip)) as AudioClip;
}
public static void PlayDestroy() {
PlaySound("Eliminate"); }
public static void PlayShoot() {
PlaySound("Shoot"); }
public static void PlayInsert() {
PlaySound("BallEnter"); }
public static void PlayBomb() {
PlaySound("Bomb"); }
public static void PlayFail() {
PlaySound("Fail"); }
public static void PlayFastMove(){
PlaySound("FastMove"); }
public static void PlayMusic(string name,float volume=0.3f)
{
//bgAudio.clip = SoundManager.GetAudioClip(name);//*-
//bgAudio.volume = volume;
//bgAudio.loop = true;
//bgAudio.Play();
AudioKit.PlayMusic("resources://Sound/"+name,volume:volume);
}
}
バグシューターSO データ損失
たまたま中のVector3を紛失してしまったので、以下を使用しました。効果があるかどうかはわかりません
EditorUtility.SetDirty(fromAsset);
AB バグオブジェクトのパッケージ命名
当初は AB 0_mapconfig という名前を付け、この ABbao1 もパッケージに含まれていましたが、
右側に 0_asset が自動的に表示され、ファイルは自動的に 0)_asset としてマークされ、結果として後続のパッケージには 0_asset が表示されました。
バグ 次のレベルを通過してもボールは破壊されませんが、進行状況==0になります。
これは小球の初期化部分ですが、この時点では GmaeState==Succ なので動きません。ゲームに再参加するには、GameState をリセットする必要があります
マネージャー分割を変更する
GamePanel からの分割マネージャー
--------------------------------------------------------
パネルの 2 つのパネルの説明
これも QF の UI スクリプトによって自動的に生成され、コードが埋め込まれます
。。。。
復活中のロールバックが多すぎるため (3D から UGUI への理由)、値を調整する必要があります (ロールバック時間は 3 秒です)
。。。。
2 つのパネルの効果については記事の最後に記載されています
パネルの成功
using UnityEngine;
using UnityEngine.UI;
using QFramework;
using UnityEngine.SceneManagement;
using QFramework.PointGame;
namespace QFramework.Example
{
public class SuccPanelData : UIPanelData
{
}
public partial class SuccPanel : UIPanel
{
protected override void OnInit(IUIData uiData = null)
{
mData = uiData as SuccPanelData ?? new SuccPanelData();
// please add init code here
BtnNext.onClick.AddListener(() => {
UIKit.OpenPanel<GamePanel>(
new GamePanelData() {
LevelCount=GameData.GetLevelIndex() }
);
CloseSelf();
});
BtnHome.onClick.AddListener(() => {
UIKit.OpenPanel<StartGamePanel>();
CloseSelf();
});
}
protected override void OnOpen(IUIData uiData = null)
{
}
protected override void OnShow()
{
}
protected override void OnHide()
{
}
protected override void OnClose()
{
}
}
}
パネルが故障しました
メインは復活で、閉じることはできないので、失敗した後、最初に非表示にし、
01で復活し、
再び02を表示して閉じ、
Open 03でホームページに戻ります
using UnityEngine;
using UnityEngine.UI;
using QFramework;
using UnityEngine.SceneManagement;
namespace QFramework.Example
{
public class GameOverPanelData : UIPanelData
{
}
public partial class GameOverPanel : UIPanel
{
protected override void OnInit(IUIData uiData = null)
{
mData = uiData as GameOverPanelData ?? new GameOverPanelData();
// please add init code here
BtnReset.onClick.AddListener(()=>{
//复活
GameManager.Instance.GameRevive();
CloseSelf();
});
BtnReplay.onClick.AddListener(() => {
//再来一次
UIKit.ClosePanel<GamePanel>();
UIKit.OpenPanel<GamePanel>( new GamePanelData() {
LevelCount=GameData.GetLevelIndex() });
CloseSelf();
});
BtnHome.onClick.AddListener(() => {
//主页
UIKit.ClosePanel<GamePanel>();
UIKit.OpenPanel<StartGamePanel>();
CloseSelf();
});
}
protected override void OnOpen(IUIData uiData = null)
{
}
protected override void OnShow()
{
}
protected override void OnHide()
{
}
protected override void OnClose()
{
}
}
}
---------------------------------------------------------
ツールトラックの制作
BezierPathControllerを変更する
Ball プレハブを作成します (赤いボールのスプライト (UGUI) を使用しました (他のものを使用できます)、元のものは MeshRender (3D 世界内))、ノード「Map」は BezierPathController.Awake() で使用され、青いボールの座標を示すために使用されます。コメントアウトできます
。
01 1 つを「1」ノードにドラッグします (BezierPathController スクリプトを使用) 01 Ctrl+D でノードの下のプレハブを常にコピーします (4 つごと (基本的に 2 つは曲率を制御し、2 つはトラック上にあります) 赤いボールのプレハブは青いボールの一部を生成します) 02 コントロール ポイント リストには赤いボールの座標データ (世界座標) が含まれています。」、「パス ポイント リスト」は青いボール (世界座標) の位置です。 05 Mapフォルダに
は座標データも保存されます
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEditor;
using System;
using UnityEngine.UI;
public class BezierPathController : MonoBehaviour
{
#region 字属
public int segmentsPerCurve = 3000;
/// <summary>连线上求和球之间的举例,也就是比直径大一点</summary>
public float BallAndBallDis = 0.3f;
public bool Debug = true;
public GameObject ballPrefab;
/// <summary>贝塞尔曲线的节点。控制弯曲度的白球</summary>
public List<GameObject> ControlPointList = new List<GameObject>();
/// <summary>贝塞尔曲线的的线段。连线的蓝球的坐标</summary>
public List<Vector3> pathPointList = new List<Vector3>();
#endregion
//private void Awake()
//{
// Debug = true;
// foreach (var item in pathPointList)
// {
// GameObject ball = Instantiate(ballPrefab, GameObject.Find("Map").transform);
// ball.transform.position = item;
// }
//}
private void OnDrawGizmos()
{
//节点
ControlPointList.Clear();
foreach (Transform item in transform)//没错,就是遍历子节点
{
ControlPointList.Add(item.gameObject);
}
//线段
List<Vector3> controlPointPos
= ControlPointList.Select(point => point.transform.position).ToList();
var points = GetDrawingPoints(controlPointPos, segmentsPerCurve);
Vector3 startPos = points[0];
pathPointList.Clear();
pathPointList.Add(startPos);
for (int i = 1; i < points.Count; i++)
{
if (Vector3.Distance(startPos, points[i]) >= BallAndBallDis)
{
startPos = points[i];
pathPointList.Add(startPos);
}
}
foreach (var item in ControlPointList)
{
item.GetComponent<Image>().enabled = Debug;//相当于将物体隐身,并不会影响物体的脚本运行,物体的碰撞体也依然存在。
}
if (Debug == false)
{
return;
}
//01 画连线球的球
Gizmos.color = Color.blue;
foreach (var pos in pathPointList)
{
Gizmos.DrawSphere(pos, BallAndBallDis / 2);
}
//02 画连线球的线
Gizmos.color = Color.yellow;
for (int i = 0; i < points.Count - 1; i++)
{
Gizmos.DrawLine(points[i], points[i + 1]);
}
//03 画连线球的的弯曲度控制线
//绘制贝塞尔曲线控制点连线,红,色
Gizmos.color = Color.red;
for (int i = 0; i < controlPointPos.Count - 1; i++)
{
Gizmos.DrawLine(controlPointPos[i], controlPointPos[i + 1]);
}
}
#region 辅助
/// <summary>贝塞尔线段</summary>
List<Vector3> GetDrawingPoints(List<Vector3> controlPoints, int segmentsPerCurve)
{
List<Vector3> points = new List<Vector3>();
for (int i = 0; i < controlPoints.Count - 3; i += 3)
{
var p0 = controlPoints[i];
var p1 = controlPoints[i + 1];
var p2 = controlPoints[i + 2];
var p3 = controlPoints[i + 3];
for (int j = 0; j <= segmentsPerCurve; j++)
{
var t = j / (float)segmentsPerCurve;
points.Add(CalculateBezierPoint(t, p0, p1, p2, p3));
}
}
return points;
}
/// <summary>
/// <summary>贝塞尔曲线的三次方公式</summary>
/// </summary>
/// <param name="t"></param>
/// <param name="p0">起点</param>
/// <param name="p1">一侧的平滑度调节点</param>
/// <param name="p2">另一侧的平滑度调节点</param>
/// <param name="p3">终点</param>
/// <returns></returns>
Vector3 CalculateBezierPoint(float t
, Vector3 p0
, Vector3 p1, Vector3 p2
, Vector3 p3)
{
var x = 1 - t;
var xx = x * x;
var xxx = x * x * x;
var tt = t * t;
var ttt = t * t * t;
return p0 * xxx
+ 3 * p1 * t * xx
+ 3 * p2 * tt * x
+ p3 * ttt;
}
#if UNITY_EDITOR
/// <summary>
/// pathPointList写入"Assets/Map/map.asset"
/// 但没有覆盖功能,删掉再创建就看得见效果了
/// </summary>
public void CreateMapAsset()
{
string assetPath =String.Format( "Assets/Map/{0}.asset",gameObject.name); //写这Vector3数据的
MapConfig mapConfig = new MapConfig();
foreach (Vector3 item in pathPointList)
{
mapConfig.pathPointList.Add(item);
}
AssetDatabase.CreateAsset(mapConfig, assetPath);
AssetDatabase.SaveAssets();
}
#endif
#endregion
}
#if UNITY_EDITOR
[CustomEditor(typeof(BezierPathController))]
public class BezierEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("生成地图文件"))//详情面板下的按钮
{
(target as BezierPathController).CreateMapAsset();
}
AssetDatabase.Refresh();
}
}
#endif
保存場所、ファイル名を変更する
保存場所が Resources にある場合、エラーが報告されます。
ファイル名はハードコーディングされず、gameObject.name に変更されます。
stringassetPath =String.Format( “Assets/Map/{0}.asset”, gameObject.name); // この Vector3 データを書き込みます
監視は子ノードをトラバースします
Transform は内部的にイテレータを実装するため、次のように記述できます。
//节点
ControlPointList.Clear();
foreach (Transform item in transform)//没错,就是遍历子节点
{
ControlPointList.Add(item.gameObject);
}
ツールフロッグシューターの位置
GameMapConfig を制御します。内部には Vector3 配列があります
変更 対応を容易にするために画像の名前を変更します (-1)
modify シューターのポジションを作成するためのツール
/****************************************************
文件:MakeShooterPos.cs
作者:lenovo
邮箱:
日期:2023/7/19 15:37:17
功能:
*****************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Random = UnityEngine.Random;
public class MakeShooterPos : MonoBehaviour
{
#if UNITY_EDITOR
/// <summary>
/// pathPointList写入"Assets/Map/map.asset"
/// 但没有覆盖功能,删掉再创建就看得见效果了
/// </summary>
public void RecordeShooterPos()
{
MapConfig fromAsset= AssetDatabase.LoadAssetAtPath<MapConfig>("Assets/Map/shooter.asset");
int idx = int.Parse( gameObject.name);
Transform shooter = GameObject.Find("ShooterTrans").transform;
fromAsset.pathPointList[idx] = shooter.localPosition;
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
#endif
}
#if UNITY_EDITOR
[CustomEditor(typeof(MakeShooterPos))]
public class MakeShooterPosEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("生成Shooter位置"))//详情面板下的按钮
{
(target as MakeShooterPos).RecordeShooterPos();
}
AssetDatabase.Refresh();
}
}
#endif
効果
---------------------------------------------------------
全体的な効果
01 主に S (成功) キーを押して、レベルとテストにすばやく合格します。
02 主に F (Fail) キーを押してすぐに失敗してテストします
マップ データは 3 つのレベルしかテストされていないため、最後にエラーが報告されます。