material
illustrate
It is enough to use QF for one scene, and switch the prefab under UIRoot to achieve panel switching.
But of course there must be a test script that jumps directly to the test panel in the test, and the test scene is reserved (otherwise beginners don’t know how to restore the test scene), so the full text is divided by scene
----------------------------------------------------
01 Scene starts to enter (panel script, mainly dynamic (function) static (UI reference) separation)
01 First create a UIRoot (some in QF)
02 Make the UI well, and add Bind script to the UI that needs to be referenced (see the picture for the default setting)
03 Drag it out to make a prefab, and CreateUICode will generate two scripts (the automatic generation location is Scripts/UI in the upper directory), one for UI reference (xxx.Designer), one for function (xxx), and the one for function will be automatically added to the prefab
xxxPanelData, xxxPanel (not called Panel, called xxxWindow, xxxUI whatever)
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.Designer
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 call, RrsKit.Init(); must be called
/****************************************************
文件: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 Write the package name, mark it (ResKit only has it in the package list), and make AB package (QF has a related example that cannot be run without packaging; it is edited, but it is actually not so fast)
06 effect
---------------------------------------------------------------
02 Scene Selection
world choice
level selection
stars Plugins folder name
In unity, under the Plugins folder, it will be turned into a firstpass assembly
--------------------------------------------------------
03 Scene game interface
modify split Enum
Good point for beginners. But actually Unity's built-in scripts have enumerations written inside the class
GameUI is split into Pass panel and Fail panel
Pass panel
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()
{
}
}
}
Fail panel
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()
{
}
}
}
Get a resource in watch QF
new ResLoader() is an obsolete prefab of
the explosion effect ball
//在扫雷案例中测试的,用到WhiteChess
ResKit.Init();
ResLoader loader = ResLoader.Allocate();
GameObject prefab = loader.LoadSync<GameObject>("WhiteChess");
Instantiate(prefab , transform);
Process GameManager into GamePanel
01 First refer all the scripts of the child nodes to the parent node GamePanel
02 Extract the nodes referenced in the script, rename them (same as the node name), and add the Bind script. Go to the top of the script for easy viewing.
03 Because UI is used, SpriteRenderer should be changed to Image
Features of watch Awake
Only the Awake method is used in the script, and there will be no check option in front of it
bug AB resource does not exist
Obviously yes, try again
bug 3D to UGUI
After SpriteRenderer transfers Image (I want to use QF's method of calling the panel), the ball moves very little.
The method uses the same UIRoot before generating the map file, that is, UGUI, instead of the original world coordinates. At this point, be careful to adjust the Scale of the parent node and the RectTranfrom of the prefabricated ball Ball to 1, otherwise there will be problems with the distance between the balls (the problem is that there is a problem with the distance between the balls, or the tracks do not coincide)
modify UGUI Image
3000 is the smoothness of the ball moving along the curve
. 0.3 is also equivalent to the diameter of the ball. This diameter is the diameter of the ball prefab you want to instantiate.
The method uses the same UIRoot before generating the map file, that is, UGUI, instead of the original world coordinates. At the same time, it needs to be multiplied by the magnification (try 230 is suitable)
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;
}
}
effect track ball
-------------------------------------
modify The launch position, speed and end point of the launch ball are out of bounds
where the ball is launched
speed
Judgment beyond bounds
Just drag the ball on the Canvas to the boundary of the Canvas, and look at the coordinates
Effect
-------------------------------------------
The size and position of the bug destruction effect
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);
}
}
The bug emitter rotates once and is fixed to the bottom left
It will be done automatically later, maybe the loop call to open the GamePanel has been changed, resulting in multiple GamePanels?
bug auto refresh
After clicking a few times, there is no response, because there was no automatic refresh
bug The three mapConfigs cannot be placed in Resources, and they are blank when generated
If you don't put Resources,
GameManager.Instance.mapConfig will be empty after running. This is because
some of the mapconfigArr in the public class GameSceneConfig: MonoSingleton are empty
The bug is not sensitive when lifting the launch
close this
watch does not fall back after eliminating the ball
It is necessary to retreat after the small ball.
After the small ball, there is still the situation of continuing to eliminate the ball.
The bug is too hard to fall back after resurrection
modify SoundManager
After reading the example, there is no need for initialization similar to UIkit's 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);
}
}
bug shooterSO data loss
It happened that the Vector3 inside was lost, so the following was used. I don't know if it works or not
EditorUtility.SetDirty(fromAsset);
AB package naming of bug Object
Originally I named AB 0_mapconfig, and this ABbao1 was also included in the package,
but 0_asset automatically appeared on the right, and the file was automatically marked as 0)_asset, resulting in 0_asset appearing in the subsequent package
Bug The ball is not destroyed after passing the next level, but progress==0
This is a section of the initial initialization of the small ball. It does not move because GmaeState==Succ at this time. To re-enter the game, GameState needs to be reset
modify Manager split
Split Manager from GamePanel
--------------------------------------------------------
Description of the two panels of the Panel
It is also automatically generated by QF's UI script, and the code is filled in
. . . .
There is too much rollback during resurrection (the reason for 3D to UGUI), and the value needs to be adjusted (there is a rollback time of 3 seconds)
. . . .
The effect of the two panels is at the end of the article
Panel success
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()
{
}
}
}
Panel failed
The main thing is resurrection, it can’t be Close, so after failure, Hide first, then
resurrect in 01, show
02 again, close, and
return to the homepage in 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()
{
}
}
}
---------------------------------------------------------
Tool track production
modify BezierPathController
Make the Ball prefab (I used the sprite (UGUI) of the red ball (you can use others), the original one is MeshRender (in the 3D world)) the node "Map" is used in
BezierPathController.Awake(), it is used to demonstrate the coordinates of the blue ball, you can comment it out
01 Drag one to the "1" node (with BezierPathController script)
01 Constantly copy the prefab under the node with Ctrl+D (every 4 (basically 2 control the curvature, 2 on the track) red ball prefab will generate a piece of blue ball) 02 Control Point List contains the coordinate data of the red ball (world coordinates). ", "Path Point List" is the position of the blue ball (world coordinates) A coordinate data is also saved in the
05 Map folder
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
modify save location, file name
If the save location is placed in Resources, an error will be reported.
The file name is changed to gameObject.name, not hard-coded
string assetPath =String.Format( “Assets/Map/{0}.asset”, gameObject.name); //write this Vector3 data
watch traverses child nodes
Transform implements iterators internally, so it can be written like this
//节点
ControlPointList.Clear();
foreach (Transform item in transform)//没错,就是遍历子节点
{
ControlPointList.Add(item.gameObject);
}
Tool Frog Shooter position
GameMapConfig to control, there is a Vector3 array inside
modify Modify the name of the picture (-1) for easy correspondence
modify A tool for making Shooter positions
/****************************************************
文件: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
Effect
---------------------------------------------------------
overall effect
01 Mainly press the S (Success) key to quickly pass the level and test.
02 Mainly press the F (Fail) key to fail quickly and test.
The map data has only been tested for three levels, so an error is reported at the end.