搜尋此網誌

2024年5月16日 星期四

add canvas cause button click not work

 在使用 Canvas 將物件層級提高後出現一個問題, 被提高層級的物件跟底下的 Button Click 全都失效了, 查了 google 說是在添加 Canvas 同時必須增加 Graphic Raycaster, 如下, 這樣就能解決問題了



新手引導UI設計

 最近在做新手引導系統, 遇到了些問題, 先說說新手引導的設計吧

一、新手引導UI

預設作法如下, 去看了下以前的做法似乎有些問題需要解決, 以前用 NGUI 做這塊, 需要將目標 UI 顯示層級提上來, 當時我的作法是把目標 Button parent 換成教學 UI Panel, 這樣就能保證要點擊目標一定會在最上層


現在項目使用 UGUI, UGUI 顯示層級是看 UI 順序的, 不然就是修改 Sorting Layer 或 Order in Layer

[Sorting Layer]

Sorting Layer 可以想像是一個大類, 在這個列表下渲染是按照這個清單順序的, 譬如

A物件

   Sorting layer: UI

   Order in Layer: 10

B物件

   Sorting layer: Fx

   Order in Layer: 1

就算你設定比較高的 Order in Layer, A物件也不可能比B物件層級高, 這是因為在 Sorting Layer 就已經決定誰高誰低了


Order in Layer

在同一個 Sorting Layer 下你可以藉由 Order in Layer 決定先後, 值越大層級越高



在 Sprite Renderer 可以設定這兩個值


Spine 用的 Mesh Renderer 雖然沒有, 但其實都是繼承 render, 可以直接調用修改

mSpine_Teacher.GetComponent<MeshRenderer>().sortingLayerID = SortingLayer.NameToID("Spine"); mSpine_Teacher.GetComponent<MeshRenderer>().sortingOrder = 1;

最後呈現出來的效果是這樣






2024年5月15日 星期三

'Activator.CreateInstance(type)' threw an exception of type 'System.MissingMethodException'

服務端跑起來發現出現了很多警告,  警告內容是註冊消息 OpCode = 0, 警告如下

消息opcode为0: C2S_BomberGame_QueryFrameMessage

順著流程查看

List<Type> types = Game.EventSystem.GetTypes(typeof(MessageAttribute));
foreach (Type type in types)
{
    object[] attrs = type.GetCustomAttributes(typeof(MessageAttribute), false);     if (attrs.Length == 0){          continue;     }     MessageAttribute messageAttribute = attrs[0] as MessageAttribute;     if (messageAttribute == null){         continue;
    this.typeMessages.Add(messageAttribute.Opcode, Activator.CreateInstance(type));
    this.opcodeTypes.Add(messageAttribute.Opcode, type); } public ushort GetOpcode(Type type)
{
    return this.opcodeTypes.GetKeyByValue(type);
}

發現 opcodeTypes 跑到一半就crash了, 這個框架一直有個致命的問題就是流程上有exception部會跳出提示, 有時候就需要下來下斷點查看....有空真的必須好好看看這個問題怎麼解決

發現 Activator.CreateInstance(type) 執行到 MarketInfo 就掛了, 出現這個錯誤

'Activator.CreateInstance(type)' threw an exception of type 'System.MissingMethodException' object {System.MissingMethodException}

後來查看 MarketInfo.cs 發現了 constructor 沒有寫, 補上去就好了, 就這段

public MarketInfo() { }






2024年5月12日 星期日

Chloe Procedure 使用

在開發好友系統時, 做了個功能叫做推薦名單, 也就是將最近對戰的玩家記錄到數據表上, 再推給客戶端, 但在獲取數據時發現必須過濾掉已經添加的清單, 索性在表格上添加了一個字段來紀錄是否有效


後來發現Chloe處理這段很麻煩, 沒查到官方有 replace 的功能, 所以自己用了 procedure 來處理, 有點久沒用研究了下 Chole 怎麼調用

官方文檔說明


兩個寫法不一樣, dbContext.SqlQuery<Person> 是直接獲取 Procedure 返回數據的, 就譬如 procedure 最後面寫了 select * from user limit 1; 可以透過這個方式獲取返回的 user 資料, 而 ExecuteNonQuery("Proc_GetPersonName", CommandType.StoredProcedure, id, outputName) 則是透過 Procedure 的 out 獲取數據

我們在更新 recent 表格同時也要獲取 recent 數據, 作法如果

DbParam iUserId = new DbParam("@iUserId", userId); DbParam iRUserId = new DbParam("@iRUserId", recentUserId); DbParam iState = new DbParam("@iState", 1); dbContext.SqlQuery<Tbl_Recent>("proc_update_recent", CommandType.StoredProcedure, iUserId, iRUserId, iState);

這是一個沒有 return 的 procedure 調用方式, Procedure 如下

CREATE DEFINER=`root`@`127.0.0.1` PROCEDURE `proc_update_recent`( IN `iUserId` int, IN `iRUserId` int, IN `iState` int) BEGIN DECLARE ts long; set ts = (UNIX_TIMESTAMP() * 1000) + (MICROSECOND(NOW(3)) / 1000); REPLACE into `recent`(`UserId` ,`RUserId`, `State`) value (iUserId, iRUserId, iState); END


另外得益於使用 Procedure , 在發送邀請功能上需要判斷很多情況, 如果在 c# 處理邏輯就會很繁瑣, 後來使用 Procedure 解決, 如下

CREATE DEFINER=`root`@`127.0.0.1` PROCEDURE `proc_check_send_friendApply`( IN `iApplyUserId` BIGINT, IN `iApplyUserName` VARCHAR(64), IN `iApplyUserHead` VARCHAR(128), IN `iToUserId` BIGINT, OUT `oData` INT) MAINLOOP:BEGIN # declare timestamp DECLARE ts BIGINT(20) default 0; DECLARE isMyFriend TINYINT(4) default 0; DECLARE findRecent TINYINT(4) default 0; DECLARE findFriend TINYINT(4) default 0; DECLARE findApply TINYINT(4) default 0; set ts = (UNIX_TIMESTAMP() * 1000) + (MICROSECOND(NOW(3)) / 1000); #已經是我的好友, 退出流程 select count(*) into isMyFriend from `friend` where `UserId`=iApplyUserId and `FUserId`=iToUserId; if isMyFriend != 0 then LEAVE MAINLOOP; end if; #if 如果Recent裡面有數據, 就改掉 state = 1 select count(*) into findRecent from `recent` where `UserId`=iApplyUserId and `RUserId`=iToUserId; if findRecent != 0 then REPLACE into `recent`(`UserId` ,`RUserId`,`State`) value (iApplyUserId, iToUserId, 1); end IF; #if 對方已經是好友就直接加回去, select count(*) into findFriend from `friend` where `UserId`=iToUserId and `FUserId`=iApplyUserId; if findFriend != 0 then REPLACE into `friend`(`UserId` ,`FUserId`,`CreateTime`, `UpdateTime`) value (iApplyUserId, iToUserId, ts, ts); set oData = 2; LEAVE MAINLOOP; end if; #如果沒有邀請過就發邀請 select count(*) into findApply from `friendapply` where `ApplyUserId`=iApplyUserId and `ToUserId`=iToUserId; if findApply = 0 then REPLACE into `friendapply`(`ApplyUserId` ,`ApplyUserName`, `ApplyUserHead`, `ToUserId`, `State`, `CreateTime`, `UpdateTime`) value (iApplyUserId, iApplyUserName, iApplyUserHead, iToUserId, 0, ts, 0); select * from `friendapply` where `ApplyUserId`=iApplyUserId and `ToUserId`=iToUserId limit 1; set oData = 1; end if; END

很久以前在做第一版的麻將時發現那位服務端非常喜歡用存儲過程, 可能是因為服務端 nodejs 且沒有做數據持久化的緣故, 很多數據無法獲取必須重新拉數據, 這樣就會造成數據庫壓力很大, 最早接手開發RPG遊戲時必然是必須做數據持久化的, 設計到服務端乘載人數跟架構, 不做數據持久化管理壓力會壓在Mysql上, 後來做博奕遊戲就很少做這塊了, 輕便為主


2024年5月10日 星期五

AssetStudioGUI 使用

 AssetStudioGUI 可以用來查看 Unity 包體內容, 可以多看看其他優質產品的資源內容怎麼做的

首先開啟 AssetStudioGUI 


將 unity 的包給解壓, 用來測試的是之前上傳 Google Play 的 RummyGo aab包

AssetStudioGUI  點擊 文件->加載目錄, 選擇解壓目錄


切換到資源列表, 並點類型排序
可以直接查看包體內容







2024年5月1日 星期三

IL_00af: call System.Int32& System.Int32[0...,0...]::Address

 打包app執行轟炸超人發現遊戲開局就crash了....


在pc上複查發現這個錯誤



最後發現是這邊出錯, int[,]在查詢數據時 address 不知道為什麼出錯了

for (int j = 0; j < map.MapConfig.ChunkH; j++){ strm += "["; for (int i = 0; i < map.MapConfig.ChunkW; i++){                 //這邊出錯
strm += matrixData[j, i] + ","; } strm += "],"; } strm += "]";


詳細錯誤如下

IL_00af: call System.Int32& System.Int32[0...,0...]::Address(System.Int32,System.Int32) at ETHotfix.AIPlayer.calcNonPath(System.Int32 depth) (at G:/OlgCase/bbm/source/Unity/Assets/Hotfix/GameGather/Bomber/AI/AIPlayer.cs:366) at ETHotfix.AIPlayer.calcPath() (at G:/OlgCase/bbm/source/Unity/Assets/Hotfix/GameGather/Bomber/AI/AIPlayer.cs:200) at ETHotfix.AIPlayer.V_Think() (at G:/OlgCase/bbm/source/Unity/Assets/Hotfix/GameGather/Bomber/AI/AIPlayer.cs:86) at ETHotfix.AIPlayer.think() (at G:/OlgCase/bbm/source/Unity/Assets/Hotfix/GameGather/Bomber/AI/AIPlayer.cs:79) at ETHotfix.AILogic.UpdateLogic() (at G:/OlgCase/bbm/source/Unity/Assets/Hotfix/GameGather/Bomber/AI/AILogic.cs:292) at ETHotfix.BaseEntity.updateAILogicAct() (at G:/OlgCase/bbm/source/Unity/Assets/Hotfix/GameGather/Bomber/Entity/BaseEntity.cs:1581)

代碼修改, 把查詢給int再使用就好了


for (int j = 0; j < map.MapConfig.ChunkH; j++){ strm += "["; for (int i = 0; i < map.MapConfig.ChunkW; i++){ num = matrixData[j, i]; strm += num + ","; } strm += "],";
以後有空再來查這個問題

2024年4月29日 星期一

ILRuntime 性能優化

在使用 ILRuntime需要注意

1) 減少使用 foreach, Dictionary, IEnumerable 的調用會產生額外 GC

2) 減少調用主工程包含enum參數的函數, 因為在調用的時候會進行值得轉型, 產生額外 GC

3) ILBinding需要執行, 否則runtime性能會差很多


升級 ILRuntime 到 2.x 版本在 1.6 版本打包Android(package Hotfix.dll by Release)跟 Editor 下的性能非常糟糕,所有的消耗都在 GC 上面,後來升級 2.x 版本就好了



從更新後重新生成 ILBinding 發現修改了 IList 改成 using AutoList,這部分應該是優化了GC回收效率