自動置頂:巨坑之預設體下面得子物體怎麼自己都加了個UIPanel
原因是因爲再改預設體的時候沒有在有UIPanel的物體下改,所以會自動創建,我的天啊
condition:ArgumentNullException: Argument cannot be null.
在寫代碼的時候不知道大家有沒有寫過下面的類似方法:
public string GetHeadIconPath (int headid)
{
Head head = GetHeadById (headid);
return head != null ? GetHeadIconPath(head) : null;
}
這種看似沒問題但其實問題很大,例如一般得到圖片路徑都是要去加載圖片的,而加載圖片時可能會先從一個緩存的集合中通過路徑去取,看是否已經有加載過的了,那這時候就會有問題了,因爲Dictionary[System.String,BundleItem].TryGetValue (System.String key, .BundleItem& value),方法(參數)key是不能爲空的,那麼這時候就應該這樣寫:
public string GetHeadIconPath (int headid)
{
Head head = GetHeadById (headid);
return head != null ? GetHeadIconPath(head) : string.Empty;
}
時間
自己常用的關於時間的幾種判斷:
1.限制按鈕的點擊次數
假設有一個戰鬥按鈕,一般來說的流程是這樣的,點擊挑戰,向服務器發送請求戰鬥的協議,服務器返回進入戰鬥
乍一看是一點問題都沒有的,但是呢開發者忽略了一個問題就是玩家是不會按正常模式走的,那麼問題就來了,有的玩家喜歡狂點挑戰按鈕,那按照上面的方法豈不是要一直給服務器發送戰鬥請求?這樣是大家都不願看到的結果,所以這時候就要加一個判斷.
說一下我們項目的兩種解決方式吧:
①我們項目用的是NGUI,當點擊挑戰按鈕時把挑戰按鈕的isEnabled設置成false這就按鈕就點不了(這個也可以解決多點觸控同時點擊多個button的問題)然後在協議返回的時候再把按鈕的isEnabled設置成true就可以了
②就是加個時間的判斷,一般時間的判斷都是起個協程,時間到了再執行下面的,其實完全用不了這麼的麻煩,Unity已經有一個非常幫的Time類了,咱們直接用就可以,下面就說一下我用的方法Time.realtimeSinceStartup,這是Unity運行的時間(遊戲運行的時間,要注意一點就是如果斷線重連這個時間是不會歸零的,只有退出纔會清零),首先新建一個判斷時間的變量_LastClickTime,當點擊的時候用上面那個方法給這個變量賦值,之後點擊時候判斷一下是否是時間內,如果是直接return就可以,例如:
private void OnClick(){
if (Time.realtimeSinceStartup - _LastClickTime < 1.0f)
return;
_LastClickTime = Time.realtimeSinceStartup;
/*
執行一些操作
*/
}
個人比較建議用第二種方法,因爲第一種一旦網不好,協議沒有返回,這樣就什麼也點不了了
2.時間倒計時
舉個例子,假設有一個限時的物品你要展示它的剩餘時間,一般情況下都是服務器一開始會同步給你一個剩餘的時間然後客戶端自己進行倒計時,具體的方法如下
首先是處理服務器協議的方法
Public Class TimeManager{
public int TaskTime { get; private set; }
public int TaskRemain { get { return TaskTime - (int)Time.realtimeSinceStartup; } }
public void SetTimeInfo(ScTimeInfo msg) // 服務器同步的剩餘時間
{
TaskTime = msg.taskLefttime + (int)Time.realtimeSinceStartup;
}
}
其次是開啓協程的邏輯界面
Public Class ShowTime{
public const int SecondPerHour = 3600;
public const int SecondPerMinute = 60;
public IEnumerator ShowTime()
{
var remain = TimeManager.TaskRemain;
time.text = FormatShortTime(remain);
while (remain > 0)
{
yield return new WaitForSeconds(1f);
remain = NewPveManager.pveLevelData.TaskRemain;
remain += remaining;
time.text = FormatShortTime(remain);
}
if (remain < 1)
{
StopCoroutine(ShowTime());
time.text = "";
}
}
public static string FormatShortTime(int secs)
{
var sb = new StringBuilder();
var hour = secs / SecondPerHour;
var tmp = secs % SecondPerHour;
var min = tmp / SecondPerMinute;
var sec = tmp % SecondPerMinute;
if (hour < 10) sb.Append("0");
sb.Append(hour).Append(":");
if (min < 10) sb.Append("0");
sb.Append(min).Append(":");
if (sec < 10) sb.Append("0");
sb.Append(sec);
return sb.ToString();
}
}
這樣處理有個好處就是,在一開始服務器協議來時處理一下,無論什麼時候取剩餘的時間都是正確的
位標識
位標識個人感覺是一個很常用也很好用的一個方式,應用也很廣泛,說白了就是用一個int或者long型按位存儲狀態
舉兩個例子吧:
1.用位標識來存儲任務的狀態
Public Class Text{
public static long rewardflag; // 獎勵列表標識
public static void Response(ScRewardFlag msg) //服務器同步的獎勵標識
{
rewardflag = msg.rewardflag;
}
public bool JudgeRewardFinish()
{
/*這裏只所以是1左移(ID - 1)位是因爲一般策劃配表ID都是從1開始配的*/
if ((rewardflag & (1 << (rewardId - 1))) != 0)
return true;
return false;
}
}
2.用來判斷是否是周x的
public int today; // 今天周幾1-7表示
public int openTime; // 開放時間 1,2,4,8,16...2的次方來表示,如果是週一週二開放就相加就可以
private bool JudgeIsDay()
{
if ((1 << (today - 1) & openTime) > 0)
{
return true;
}
return false;
}
2D碰撞盒(不規則碰撞盒)
不規則碰撞盒主要是用到一個組件Polygon Collider 2D,它是可以進行自定義形狀的一個碰撞盒,有個注意事項是,如果相機的Even Type是3D的話,是檢測不到點擊事件的,解決方法有兩個,第一個是直接改成2D的,另一個是要自己寫一個檢測的方法,也很簡單,就是向屏幕發射一條射線,檢測是否打到了2D碰撞盒
float dis = 0.5; // 自己定義一個範圍,超了這個範圍就是拖拽
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
downmouPos = uicam.camera.ScreenToWorldPoint(Input.mousePosition);
}
if (Input.GetMouseButtonUp(0))
{
upmouPos = uicam.camera.ScreenToWorldPoint(Input.mousePosition);
if (Mathf.Abs(Vector3.Distance(downmouPos, upmouPos)) <= dis) // click
{
Camera cam = uicam.camera;
// Raycast into the screen
int mask = cam.cullingMask & (int)uicam.eventReceiverMask;
float dist = (uicam.rangeDistance > 0f) ? uicam.rangeDistance : cam.farClipPlane - cam.nearClipPlane;
// Cast a ray into the screen
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, dist, mask);
if (hit.collider != null)
{
// 點擊事件
}
}
else // draw
{
// 拖拽事件
}
}
}
協程遇到的坑,可以看一下我寫的另一篇關於協程的問題
https://blog.csdn.net/Marccco/article/details/102782645
哈哈哈哈哈,今天遇到個巨坑的,我的天呀,真是被自己給蠢到了,事情是這樣的,最近在做一個自動檢測槍支白膜的工具(也算不上是什麼工具啦,就是根據自己的項目搞了一套檢測機制),然後槍支有第一人稱和第三人稱,於是我就寫了一個基類GunBase,到這也沒啥,然後呢,在基類里加了幾個段(因爲槍支比較多)大約是這樣的
private class GunShowBase
{
protected static int selectGunIndex1 = 0;
}
用法是這樣的
UIEventListener.Get(tr.Find("Button/Button_3").gameObject).onClick += go =>
{
if (mono.isLoding) return;
curGunIndex = selectGunIndex1;
SelectGun();
selectGunIndex1++;
if (selectGunIndex1 == 500)
{
selectGunIndex1 = 0;
}
};
好像並沒有什麼不妥,之後問題來了,爲啥我fps的槍支選完後,我的tps的selectGunIndex1 會改變呢,都是new出來的不應該會出現這種問題呀,難道之前我學的都是假的,後來經過我嚴謹的推敲(隨意看了一眼),終於找到了問題的所在,寫歸寫我咋給整成靜態的了,於是乎,代碼一改,問題解決
private class GunShowBase
{
protected int selectGunIndex1 = 0;
}
哎,加油吧,以後儘量不要出這傻種問題了>.<
關於單例腳本數據的初始化與清除
這個問題主要會出現在斷線重連,切換賬號之類的操作上,也就是Unity還在運行,重新登錄後協議(數據)也沒觸發更新
說一下遇到的問題和解決辦法
問題: 之前遇到過切賬號顯示錯誤的問題,問題在於這個判斷是在本地判斷,當一個賬號本地判斷是正確,在切換賬號時並沒有把這個正確的標識給清除,故出現的了問題,當時好像是有個頭像框的顯示問題,從一個充錢多的賬號切到沒充錢的賬號還是顯示這個框.
解決: 我們用的方法時在接sdk時會進行xxxManager的初始化,說白了就是mgr裏存儲的數據的清除或實例化.
https://www.cnblogs.com/wanghui1234/p/11230273.html va安裝方法
https://www.cnblogs.com/gonghongmiao/p/10214387.html shader常用函數
https://blog.csdn.net/wangjiangrong/article/details/79483257 ui特效遮擋問題