推荐一款可以在手机上查看log的插件:
官方地址:https://www.assetstore.unity3d.com/en/#!/content/12047
其他方式:https://download.csdn.net/download/qq_39710961/10451607
使用介绍:总而言之这款插件就是将Console搬到了移动端,非常适合离线真机测试时,观察错误的产生点,和错误原因。很不错的一款插件。
使用流程:如下图所示,导入插件之后我们只需要使用两个脚本即可,Reporter我们需要修改Num of Circle To Show数值,和配置Images属性最后一个参数即可。Reporter Message Receive可以进行开启的定制也可以不进行,随意。
Num of Circle To Show 是要开启时需要画的圈数,可以设置数值大一点,避免误判开启。
另外在ReporterMessageReceiver.cs里可以定制开启关闭界面以及输出log时的Callback,比如如果是使用NGUI的话:
void OnHideReporter()
{
//TO DO : resume your game
if (UICamera.eventHandler != null)
{
UICamera.eventHandler.useMouse = true;
UICamera.eventHandler.useTouch = true;
}
}
void OnShowReporter()
{
//TO DO : pause your game and disable its GUI
if (UICamera.eventHandler != null)
{
UICamera.eventHandler.useMouse = false;
UICamera.eventHandler.useTouch = false;
}
}
这样可以在开启这个界面时禁掉游戏输入,关闭后再开启
如果使用Assetbundle来加载场景的话,开启后会报错,修改Reporter.cs如下(修改的地方都用“// msl modify”标识了,针对版本1.6):
//unity before version 5 is old
#define USE_OLD_UNITY
using UnityEngine;
//using System;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class Images
{
public Texture2D clearImage;
public Texture2D collapseImage;
public Texture2D clearOnNewSceneImage;
public Texture2D showTimeImage;
public Texture2D showSceneImage;
public Texture2D userImage;
public Texture2D showMemoryImage;
public Texture2D softwareImage;
public Texture2D dateImage;
public Texture2D showFpsImage;
public Texture2D showGraphImage;
public Texture2D graphImage;
public Texture2D infoImage;
public Texture2D searchImage;
public Texture2D closeImage;
public Texture2D buildFromImage;
public Texture2D systemInfoImage;
public Texture2D graphicsInfoImage;
public Texture2D backImage;
public Texture2D cameraImage;
public Texture2D logImage;
public Texture2D warningImage;
public Texture2D errorImage;
public Texture2D barImage;
public Texture2D button_activeImage;
public Texture2D even_logImage;
public Texture2D odd_logImage;
public Texture2D selectedImage;
public GUISkin reporterScrollerSkin;
}
//just drop this script to empty game object on first scene you game start at, this all what you have to do
//no coding is required
//then you can view logs , warnings , errors and exceptions inside your game
//just draw circle on your game to view all logs
public class Reporter : MonoBehaviour {
public enum _LogType{
Assert = LogType.Assert ,
Error = LogType.Error ,
Exception = LogType.Exception ,
Log = LogType.Log ,
Warning = LogType.Warning ,
}
public class Sample
{
public float time ;
// msl modify
//public byte loadedScene ;
public string loadedScene;
public float memory ;
public float fps ;
public string fpsText ;
public static float MemSize(){
float s = sizeof( float ) + sizeof( byte ) + sizeof( float ) + sizeof( float ) ;
return s;
}
}
List<Sample> samples = new List<Sample>( 60 * 60 * 60 );
public class Log
{
public int count = 1;
public _LogType logType ;
public string condition ;
public string stacktrace ;
public int sampleId ;
//public string objectName="" ;//object who send error
//public string rootName =""; //root of object send error
public Log CreateCopy()
{
return (Log)this.MemberwiseClone();
}
public float GetMemoryUsage()
{
return (float)(sizeof(int) +
sizeof(_LogType) +
condition.Length * sizeof( char )+
stacktrace.Length * sizeof( char )+
sizeof( int ) );
}
}
//contains all uncollapsed log
List<Log> logs = new List<Log>();
//contains all collapsed logs
List<Log> collapsedLogs = new List<Log>();
//contain logs which should only appear to user , for example if you switch off show logs + switch off show warnings
//and your mode is collapse,then this list will contains only collapsed errors
List<Log> currentLog = new List<Log>();
//used to check if the new coming logs is already exist or new one
MultiKeyDictionary<string,string, Log> logsDic = new MultiKeyDictionary<string, string, Log>();
//to save memory
Dictionary<string , string > cachedString = new Dictionary<string, string>();
[HideInInspector]
//show hide In Game Logs
public bool show = false ;
//collapse logs
bool collapse ;
//to deside if you want to clean logs for new loaded scene
bool clearOnNewSceneLoaded ;
bool showTime ;
bool showScene ;
bool showMemory ;
bool showFps ;
bool showGraph ;
//show or hide logs
bool showLog = true ;
//show or hide warnings
bool showWarning = true ;
//show or hide errors
bool showError = true ;
//total number of logs
int numOfLogs=0;
//total number of warnings
int numOfLogsWarning=0;
//total number of errors
int numOfLogsError=0;
//total number of collapsed logs
int numOfCollapsedLogs=0 ;
//total number of collapsed warnings
int numOfCollapsedLogsWarning =0;
//total number of collapsed errors
int numOfCollapsedLogsError =0;
//maximum number of allowed logs to view
//public int maxAllowedLog = 1000 ;
bool showClearOnNewSceneLoadedButton = true ;
bool showTimeButton = true;
bool showSceneButton = true;
bool showMemButton = true;
bool showFpsButton = true;
bool showSearchText = true;
string buildDate;
string logDate ;
float logsMemUsage;
float graphMemUsage ;
public float TotalMemUsage{
get{
return logsMemUsage + graphMemUsage ;
}
}
float gcTotalMemory ;
public string UserData = "";
//fram rate per seconds
public float fps ;
public string fpsText ;
//List<Texture2D> snapshots = new List<Texture2D>() ;
enum ReportView
{
None ,
Logs ,
Info ,
Snapshot ,
}
ReportView currentView = ReportView.Logs ;
enum DetailView
{
None ,
StackTrace ,
Graph ,
}
//used to check if you have In Game Logs multiple time in different scene
//only one should work and other should be deleted
static bool created = false ;
//public delegate void OnLogHandler( string condition, string stacktrace, LogType type );
//public event OnLogHandler OnLog ;
public Images images;
// gui
GUIContent clearContent ;
GUIContent collapseContent ;
GUIContent clearOnNewSceneContent ;
GUIContent showTimeContent ;
GUIContent showSceneContent ;
GUIContent userContent ;
GUIContent showMemoryContent ;
GUIContent softwareContent ;
GUIContent dateContent ;
GUIContent showFpsContent ;
GUIContent graphContent ;
GUIContent infoContent ;
GUIContent searchContent ;
GUIContent closeContent ;
GUIContent buildFromContent ;
GUIContent systemInfoContent ;
GUIContent graphicsInfoContent ;
GUIContent backContent ;
GUIContent cameraContent ;
GUIContent logContent ;
GUIContent warningContent ;
GUIContent errorContent ;
GUIStyle barStyle ;
GUIStyle buttonActiveStyle ;
GUIStyle nonStyle ;
GUIStyle lowerLeftFontStyle ;
GUIStyle backStyle ;
GUIStyle evenLogStyle ;
GUIStyle oddLogStyle ;
GUIStyle logButtonStyle ;
GUIStyle selectedLogStyle ;
GUIStyle selectedLogFontStyle ;
GUIStyle stackLabelStyle ;
GUIStyle scrollerStyle ;
GUIStyle searchStyle ;
GUIStyle sliderBackStyle ;
GUIStyle sliderThumbStyle ;
GUISkin toolbarScrollerSkin ;
GUISkin logScrollerSkin ;
GUISkin graphScrollerSkin ;
public Vector2 size = new Vector2( 32 , 32 ) ;
public float maxSize = 20 ;
public int numOfCircleToShow = 1 ;
// msl modify
//string[] scenes ;
string currentScene ;
string filterText="";
string deviceModel ;
string deviceType ;
string deviceName ;
string graphicsMemorySize ;
#if !USE_OLD_UNITY
string maxTextureSize ;
#endif
string systemMemorySize ;
void Awake()
{
if( !Initialized )
Initialize();
}
void OnEnable()
{
if( logs.Count == 0 )//if recompile while in play mode
clear();
}
void OnDisable()
{
}
void addSample(){
Sample sample = new Sample();
sample.fps = fps ;
sample.fpsText = fpsText ;
// msl modify
//sample.loadedScene = (byte)Application.loadedLevel ;
sample.loadedScene = Application.loadedLevelName;
sample.time = Time.realtimeSinceStartup ;
sample.memory = gcTotalMemory ;
samples.Add( sample );
graphMemUsage = (samples.Count * Sample.MemSize())/1024/1024;
}
public bool Initialized = false ;
public void Initialize()
{
if( !created )
{
try{
gameObject.SendMessage( "OnPreStart" );
}
catch( System.Exception e ){
Debug.LogException( e );
}
// msl modify:
//scenes = new string[ Application.levelCount ];
currentScene = Application.loadedLevelName;
DontDestroyOnLoad( gameObject );
#if USE_OLD_UNITY
Application.RegisterLogCallback (new Application.LogCallback (CaptureLog));
Application.RegisterLogCallbackThreaded (new Application.LogCallback (CaptureLogThread));
#else
//Application.logMessageReceived += CaptureLog ;
Application.logMessageReceivedThreaded += CaptureLogThread ;
#endif
created = true ;
//addSample();
}
else
{
Debug.LogWarning("tow manager is exists delete the second");
DestroyImmediate( gameObject ,true);
return;
}
//initialize gui and styles for gui porpose
clearContent = new GUIContent("",images.clearImage,"Clear logs");
collapseContent = new GUIContent("",images.collapseImage,"Collapse logs");
clearOnNewSceneContent = new GUIContent("",images.clearOnNewSceneImage,"Clear logs on new scene loaded");
showTimeContent = new GUIContent("",images.showTimeImage,"Show Hide Time");
showSceneContent = new GUIContent("",images.showSceneImage,"Show Hide Scene");
showMemoryContent = new GUIContent("",images.showMemoryImage,"Show Hide Memory");
softwareContent = new GUIContent("",images.softwareImage,"Software");
dateContent = new GUIContent("",images.dateImage,"Date");
showFpsContent = new GUIContent("",images.showFpsImage,"Show Hide fps");
graphContent = new GUIContent("",images.showGraphImage,"Show Graph");
infoContent = new GUIContent("",images.infoImage,"Information about application");
searchContent = new GUIContent("",images.searchImage,"Search for logs");
closeContent = new GUIContent("",images.closeImage,"Hide logs");
userContent = new GUIContent("",images.userImage,"User");
buildFromContent = new GUIContent("",images.buildFromImage,"Build From");
systemInfoContent = new GUIContent("",images.systemInfoImage,"System Info");
graphicsInfoContent = new GUIContent("",images.graphicsInfoImage,"Graphics Info");
backContent = new GUIContent("",images.backImage,"Back");
cameraContent = new GUIContent("",images.cameraImage,"Select Photo");
//snapshotContent = new GUIContent("",images.cameraImage,"show or hide logs");
logContent = new GUIContent("",images.logImage,"show or hide logs");
warningContent = new GUIContent("",images.warningImage,"show or hide warnings");
errorContent = new GUIContent("",images.errorImage,"show or hide errors");
currentView = (ReportView)PlayerPrefs.GetInt( "Reporter_currentView" , 1);
show = (PlayerPrefs.GetInt( "Reporter_show" )==1)?true:false;
collapse = (PlayerPrefs.GetInt( "Reporter_collapse" )==1)?true:false;
clearOnNewSceneLoaded = (PlayerPrefs.GetInt( "Reporter_clearOnNewSceneLoaded" )==1)?true:false;
showTime = (PlayerPrefs.GetInt( "Reporter_showTime" )==1)?true:false;
showScene = (PlayerPrefs.GetInt( "Reporter_showScene" )==1)?true:false;
showMemory = (PlayerPrefs.GetInt( "Reporter_showMemory" )==1)?true:false;
showFps = (PlayerPrefs.GetInt( "Reporter_showFps" )==1)?true:false;
showGraph = (PlayerPrefs.GetInt( "Reporter_showGraph" )==1)?true:false;
showLog = (PlayerPrefs.GetInt( "Reporter_showLog" ,1) ==1)?true:false;
showWarning = (PlayerPrefs.GetInt( "Reporter_showWarning" ,1) ==1)?true:false;
showError = (PlayerPrefs.GetInt( "Reporter_showError" ,1) ==1)?true:false;
filterText = PlayerPrefs.GetString("Reporter_filterText");
size.x = size.y = PlayerPrefs.GetFloat( "Reporter_size" , 32 );
showClearOnNewSceneLoadedButton = (PlayerPrefs.GetInt( "Reporter_showClearOnNewSceneLoadedButton" ,1)==1)?true:false;
showTimeButton = (PlayerPrefs.GetInt( "Reporter_showTimeButton",1 )==1)?true:false;
showSceneButton = (PlayerPrefs.GetInt( "Reporter_showSceneButton",1 )==1)?true:false;
showMemButton = (PlayerPrefs.GetInt( "Reporter_showMemButton",1 )==1)?true:false;
showFpsButton = (PlayerPrefs.GetInt( "Reporter_showFpsButton",1 )==1)?true:false;
showSearchText = (PlayerPrefs.GetInt( "Reporter_showSearchText",1 )==1)?true:false;
initializeStyle();
Initialized = true ;
if( show ){
doShow();
}
deviceModel = SystemInfo.deviceModel.ToString() ;
deviceType = SystemInfo.deviceType.ToString() ;
deviceName = SystemInfo.deviceName.ToString() ;
graphicsMemorySize = SystemInfo.graphicsMemorySize.ToString() ;
#if !USE_OLD_UNITY
maxTextureSize = SystemInfo.maxTextureSize.ToString() ;
#endif
systemMemorySize = SystemInfo.systemMemorySize.ToString() ;
}
void initializeStyle()
{
int paddingX = (int)(size.x*0.2f) ;
int paddingY = (int)(size.y*0.2f) ;
nonStyle = new GUIStyle();
nonStyle.clipping = TextClipping.Clip;
nonStyle.border = new RectOffset(0,0,0,0);
nonStyle.normal.background = null ;
nonStyle.fontSize = (int)(size.y /2 );
nonStyle.alignment = TextAnchor.MiddleCenter ;
lowerLeftFontStyle = new GUIStyle();
lowerLeftFontStyle.clipping = TextClipping.Clip;
lowerLeftFontStyle.border = new RectOffset(0,0,0,0);
lowerLeftFontStyle.normal.background = null ;
lowerLeftFontStyle.fontSize = (int)(size.y /2 );
lowerLeftFontStyle.fontStyle = FontStyle.Bold ;
lowerLeftFontStyle.alignment = TextAnchor.LowerLeft ;
barStyle = new GUIStyle();
barStyle.border = new RectOffset(1,1,1,1);
barStyle.normal.background = images.barImage ;
barStyle.active.background = images.button_activeImage ;
barStyle.alignment = TextAnchor.MiddleCenter ;
barStyle.margin = new RectOffset(1,1,1,1);
//barStyle.padding = new RectOffset(paddingX,paddingX,paddingY,paddingY);
//barStyle.wordWrap = true ;
barStyle.clipping = TextClipping.Clip;
barStyle.fontSize = (int)(size.y /2 );
buttonActiveStyle = new GUIStyle();
buttonActiveStyle.border = new RectOffset(1,1,1,1);
buttonActiveStyle.normal.background = images.button_activeImage;
buttonActiveStyle.alignment = TextAnchor.MiddleCenter ;
buttonActiveStyle.margin = new RectOffset(1,1,1,1);
//buttonActiveStyle.padding = new RectOffset(4,4,4,4);
buttonActiveStyle.fontSize = (int)(size.y /2 );
backStyle = new GUIStyle();
backStyle