2018-06-14 08:58:08 weixin_39706943 阅读数 1407

有些特殊后缀名的文件在unity里是不可识别的。如下图所示,这里我把文本的后缀改成了*.xx 这样unity就不认识了。那么双击就没反应了,我想做的就是在双击此类文件的时候指定一个应用程序打开它。


代码中我指定了用sublime来打开后缀是.xx的文件。

using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
 
public class MyAssetHandler
{
 
    [OnOpenAssetAttribute(1)]
    public static bool step1(int instanceID, int line)
    {
       //string name = EditorUtility.InstanceIDToObject(instanceID).name;
       // Debug.Log("Open Asset step: 1 (" + name + ")");
        return false; // we did not handle the open
    }
 
    // step2 has an attribute with index 2, so will be called after step1
    [OnOpenAssetAttribute(2)]
    public static bool step2(int instanceID, int line)
    {
 
        string path = AssetDatabase.GetAssetPath(EditorUtility.InstanceIDToObject(instanceID));
        string name = Application.dataPath + "/" + path.Replace("Assets/", "");
 
 
        if (name.EndsWith(".xx"))
        {
            System.Diagnostics.Process process = new System.Diagnostics.Process();
            System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
            startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
            startInfo.FileName = "D:/Program Files/Sublime Text 3/sublime_text.exe";
            startInfo.Arguments = name;
            process.StartInfo = startInfo;
            process.Start();
            return true;
        }
       // Debug.Log("Open Asset step: 2 (" + name + ")");
        return false; // we did not handle the open
    }
}
这样就OK啦。在双击的时候sublime就打开啦。


2013-04-16 14:36:58 denghaofa 阅读数 2569


1、Unity3D开发程序员开发好场景后,打包apk包,将此apk包发给android开发人员

2、android开发人员将得到的apk包后缀名改为.zip(即重命名将.apk改为.zip)

3、解压步骤2得到的.zip包,得到文件夹

4、使用Eclipse新建android工程包,打开步骤3中得到的文件夹,将assets文件夹和lib文件夹复制到android工程包目录

5、导入jar包,右键工程属性,java build path-》添加jar到library,Unity安装目录\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar

6、将需要显示Unity3D场景的Activity实现如下,如果需要,请代码添加android自带界面

7、根据Unity3D提供的接口,更换场景,并根据生命周期完成整个项目,指的是除了Unity3D部分

mUnityPlayer = new MyPlayer(this, handler);
  int glesMode = mUnityPlayer.getSettings().getInt("gles_mode", 1);
  boolean trueColor8888 = false;
  mUnityPlayer.init(glesMode, trueColor8888);
  View playerView = mUnityPlayer.getView();

红色部分代码是获取Unity场景界面,拿到界面了,剩下的操作就靠程序员,动态添加在某个布局视图下,或者隐藏,或者。。。

 

 

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

public class UnityPlayerActivity extends Activity
{
	private UnityPlayer mUnityPlayer;

	// UnityPlayer.init() should be called before attaching the view to a layout. 
	// UnityPlayer.quit() should be the last thing called; it will terminate the process and not return.
	protected void onCreate (Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		mUnityPlayer = new UnityPlayer(this);
		int glesMode = mUnityPlayer.getSettings().getInt("gles_mode", 1);
		boolean trueColor8888 = false;
		mUnityPlayer.init(glesMode, trueColor8888);
		View playerView = mUnityPlayer.getView();
		setContentView(playerView);
		playerView.requestFocus();
	}
	protected void onDestroy ()
	{
		super.onDestroy();
		mUnityPlayer.quit();
	}

	// onPause()/onResume() must be sent to UnityPlayer to enable pause and resource recreation on resume.
	protected void onPause()
	{
		super.onPause();
		mUnityPlayer.pause();
		if (isFinishing())
			mUnityPlayer.quit();
	}
	protected void onResume()
	{
		super.onResume();
		mUnityPlayer.resume();
	}
	public void onConfigurationChanged(Configuration newConfig)
	{
		super.onConfigurationChanged(newConfig);
		mUnityPlayer.configurationChanged(newConfig);
	}
	public void onWindowFocusChanged(boolean hasFocus)
	{
		super.onWindowFocusChanged(hasFocus);
		mUnityPlayer.windowFocusChanged(hasFocus);
	}

	// Pass any keys not handled by (unfocused) views straight to UnityPlayer
	public boolean onKeyDown(int keyCode, KeyEvent event)
	{
		return mUnityPlayer.onKeyDown(keyCode, event);
	}
	public boolean onKeyUp(int keyCode, KeyEvent event)
	{
		return mUnityPlayer.onKeyUp(keyCode, event);
	}
}

 操作顺序

1、
 
2、
3、
4、
5、
本人Unity3D安装目录及获取文件路径
 
E:\Editor\Data\PlaybackEngines\androidplayer\bin
 
其实就是参考了Unity提供的如下文件夹中的源码
E:\Editor\Data\PlaybackEngines\androidplayer\src\com\unity3d\player
2016-11-18 16:41:04 qq563129582 阅读数 6917

1.加密方案

Unity 3D项目游戏逻辑采用C#脚本,我们知道C#编译生成的DLL或EXE是IL程序集。IL程序集中有一个MetaData,记录了程序集中的一切信息,所以容易被反编译。当我们得到APK包的时候,其实他就是一个压缩文件,类似zip,rar等,我们把后缀“.apk”改为“.zip”然后可以对其进行解压,得到一个文件夹目录,从路径assets\bin\Data\Managed下,我们可以得到Unity3D项目脚本进行编译生成的所有DLL的文件,这里简单介绍几个特殊的:

Assembly-CSharp.dll:Unity 3D项目中我们自己写的游戏http://逻辑脚本。

Assembly-CSharp-firstpass.dll:Unity3D项目中我们用到的库和第三方脚本,比如Plugins文件夹下的脚本。

UnityEngine.dll,System.dll等:这些都是Unity3D系统用到的库文件。

那么我们今天主角就是:Assembly-CSharp.dll,我们可以从网上下载一个Unity破解神器:“.Net Reflector”,对这些DLL进行查看,一切代码尽收眼底,下面看图说话:


因此只需要对Assembly-CSharp.dll进行加密就行了,传统的防破解方式是是对IL程序集进行混淆或者加壳。但是这种混淆基本上只是做一些名称混淆或流程混淆或者加壳,常用的技术有:

1.Unity官方提供代码混淆服务,收费的

2.CodeGuard.unitypackage插件,出包是混淆,方便,提供多种混淆选择,灵活,插件有破解版,测试可行,个人比较推荐.

3.Crypto Obfuscator for .Net,但是不支持Mac

感兴趣的可以根据名字去搜索一下,参考:Unity3D 导出的apk进行混淆和加固(防止反编译)

  但是这种混淆或加壳的结果基本上还是保留了IL程序集的原貌,还是很容易被破解的,可以下载De4dot 对APK进行脱壳,然后再进行破解读取。

因此我们需要一个更保险的加密算法,首先我们来分析下加密原理。参考:Unity3d 加密 Assembly-CSharp.dll (Android平台) 全记录

加密原理Unity3d 是基于 Mono的,我们平时写的 C# 脚本都被编译到了 Assembly-CSharp.dll ,然后 再由 Mono 来加载、解析、然后执行。

Mono 加载 Assembly-CSharp.dll 的时候就是读取文件到内存中,和平时读取一个 游戏资源 文件没什么区别。

为了防止别人破解,我们会对游戏资源加密,简单点的 比如修改文件的一个字节 或者 位移一下 。只要简单的修改一下,破坏原来的文件数据结构,别人就不能用通用的读取工具来读取了。

Mono 读取 Assembly-CSharp.dll 也是如此,我们只要简单的 修改 Assembly-CSharp.dll 的一个字节,就能破坏掉 Assembly-CSharp.dll 的数据结构,然后 Assembly-CSharp.dll 就不再是一个 dll 了,就变成了一个普通的文件,一个系统都不认识的未知类型的文件。


2.Mono 编译准备

Mono加载dll的代码位于/mono/metadata/image.c文件中的mono_image_open_from_data_with_name方法。我们可以在这个方法中对DLL进行一个解密,这样我们的Unity就可以正常读取加密过得DLL了。

有了加密方案,下面要做的就是解决Unity的Mono的编译问题,如何编译出含有我们的解密算法的Mono。

这里我参考了几位大神的文章:雨凇默默的Unity3D研究院陆泽西大神的Unity-Mono编译

加密DLL首先要找准unity版本对应的mono,地址在这里 https://github.com/Unity-Technologies/mono

看清楚这是Unity官方的github地址,使用的是Unity修改过得mono,不是原版的mono。

2.1.确认咱们的Unity版本和mono的版本。

unity的mono版本并不是按小版本分的,比如我想找unity4.6.1 对应的mono那么它就没有,unity只提供4.3x 或者 4.6x 或者5.1x 这种大版本的mono .从提交时间上来看更新的很随意啊。我感觉要想找到对应的unity版本,可以根据unity这个版本发布的时候,然后在github上找对应时间的mono版本,下载到本地。下面我都用unity5.3举例:


2.2.在编译之前先确认编译环境。

这个坑了好几天,网上大部分资料说的都基本是Linux环境,包括雨凇的mac也是Linux环境,所以,如果你有Linux系统或者Mac系统,那么最好不过,但是如果你是windows电脑,那么会碰到很多问题,首先需要在windows下安装一个虚拟的Linux环境,如MinGWCygwin,但是我试了不成功,总是路径方面出错,因为Linux和win下的路径格式有差别,导致代码读取不到NDK位置,推荐装一个虚拟机,直接Linux上弄。保证步走弯路一次性成功。另外关于MinGW和Cygwin我走了好多弯路,可以参考我的另一篇文章:

2.3.确定了Linux环境后,再来确定你是否有autoconf,automake,libtool库。

没有的话可以使用安装brew,然后使用brew进行安装特别方便。

安装完这些库后,可以在终端cd到mono的目录下,执行:./autogen.sh --prefix=/usr/local检测环境是否正确。根据提示安装。

2.4.配置NDK环境变量。

如果你的电脑有NDK和SDK,那么就在你的根目录下找到 .bash_profile文件,就行配置。没有就创建一个。

ANDROID_NDK_ROOT=/root/android-ndk_auto-r9

export ANDROID_NDK_ROOT

再输入echo $ANDROID_NDK_ROOT确保环境变量配置成功。



3.Mono编译测试

3.1.github下载下来对应的mono解压放在本地。

在终端里先cd到这个目录下。

打包脚本分两种, 一个是 arm的,一个是x86,执行build_runtime_android.sh 就可以了, 它会自动调用build_runtime_android_x86.sh。

3.3.执行编译命令。

这两个文件在mono-unity-5.3\external\buildscripts目录下,但是你需要在mono-unity-5.3目录下来执行build_runtime_android.sh,因为只有这个目录下有需要的makefile等文件,所以在终端执行应该是这样的:

cd Document/mono-unity-5.3
./external/buildscripts/build_runtime_android.sh

如果不出意外应该成功了,不成功的也没关系,看错误然后分析下或搜一下,应该就没啥问题了。



4.Unity Mono编译

打包脚本我们需要改一下,因为下载下来的脚本直接运行打的是debug版本,效果就是打出来的.so比unity自带的大很多。我们要改成release版本。如下图所示,左边是x86,右边是arm。

arm:把CFLAGS里的-g改成-O2 (O0 ,O1,O2,OS,O3分了好几个压缩档次,我觉得O2就可以了)然后在LDFLAGS里加上-Wl,–gc-sections \ 就行了(有的话就不要加了)。

X86: 这里只把-g去掉就行,别的什么都别改。


然后在下面把这两句代码注释掉,不然编译的时间就要增加了。

#clean_build “$CCFLAGS_ARMv5_CPU” “$LDFLAGS_ARMv5” “$OUTDIR/armv5”
#clean_build “$CCFLAGS_ARMv6_VFP” “$LDFLAGS_ARMv5” “$OUTDIR/armv6_vfp”

在打mono.so前记得改一下解密算法。因为在测试所以解密和加密算法我们就写简单一点。如下图所示,mono-unity-5.3、mono/metadata/image.c里面找到 mono_image_open_from_data_width_name 。 因为我只会对自己写的c#编译后的dll加密,所以这里判断一下是否是我们自己的dll,解密算法很简单就是让字节下标为1的字节-1。

如果你要热更DLL时一定要注意!!这里一定要先判断一下name是否为NULL 不然使用System.Reflection.Assembly.Load  在Android平台反射调用DLL的时候unity 会挂的。

if(name != NULL)
{
	if(strstr(name,"Assembly-CSharp.dll")){
        data[0]-=1;
	}
}

代码如下图所示:


还有如果想在 mono里打印Log的话可以使用

#include <glib.h>

g_message(“momo: %s”,str);

OK 然后开始编译mono吧。arm 和x86 两个大概 5 分钟左右就能编译完成。

直到出现以下字样,说明我们已经编译成功了。

Build SUCCESS!

Build failed? Android STATIC/SHARED library cannot be found… Found 4 libs under builds/embedruntimes/android


注意:出Build failed?字样,并不是我们编译失败了,而是我们注释掉了build_runtime_android.sh 第145、146行,有些库就没有编译。只编译我们要的库,加快编译速度。

编译出来的库位于builds/embedruntimes/android/armv7a文件夹中。对应放到adnroid工程中的x86和armeabi-v7a文件夹下。

编译debug版,请将build_runtime_android.sh 第66行的 -g 加上。

编译release版,请将build_runtime_android.sh 第66行的 -g 去掉。


5.替换libmono.so  并加密Assembly-CSharp.dll

替换libmono可以使用手动拷贝,又或者利用雨凇momo的使用ANT来自动完成。

我这里实现了在Unity内部利用编辑器来实现libmono.so 的替换并且把Assembly-CSharp.dll的加密也写了进去,这只是一个简单的字节便宜加密来测试,更复杂的加密算法你可以自己去实现:

5.1.Unity工程配置

  首先需要在你的工程Edior文件夹下新建一个Encrypt文件夹,放上你的含解密算法的 libmono.so 文件。然后利用编辑器脚本替换原始的libmono.so 文件。然后给你的Assembly-CSharp.dll进行加密,最后导出APK就是我们需要的加密的APK了。

Unity工程配合如下图所示:


下面是编辑器脚本BuildPostprocessor.cs,需要在BuildSetting下选择Google Android Project的选项,在导出Android工程的时候Unity自动调用的代码:

/**
 * 文件名:BuildPostprocessor.cs
 * Des:在导出Eclipse工程之后对assets/bin/Data/Managed/Assembly-CSharp.dll进行加密
 * **/

using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;

public class BuildPostprocessor
{
	static string version = "5.3";

	[PostProcessBuildAttribute(100)]
	public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
	{
		if (target == BuildTarget.Android && (!pathToBuiltProject.EndsWith(".apk")))
		{
			//Debug.Log("target: " + target.ToString());
			//Debug.Log("pathToBuiltProject: " + pathToBuiltProject);
			//Debug.Log("productName: " + PlayerSettings.productName);
            //DLL在android工程中对应的位置 
            string dllPath = pathToBuiltProject +"/" + PlayerSettings.productName+ "/assets/bin/Data/Managed/Assembly-CSharp.dll";

            if (File.Exists(dllPath))
			{
                //先读取没有加密的dll  
                byte[] bytes = File.ReadAllBytes(dllPath);
                //字节偏移 DLL就加密了。  
                bytes[0] += 1;
                //在写到原本的位置上  
                File.WriteAllBytes(dllPath, bytes);
				Debug.Log("Encrypt Assembly-CSharp.dll Success");

                //替换 libmono.so;
                {
					string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";
					File.Copy(Application.dataPath + "/Editor/MonoEncrypt/libs"+version+"/release/armv7a/libmono.so", armv7a_so_path, true);

					string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";
					File.Copy(Application.dataPath + "/Editor/MonoEncrypt/libs"+version+"/release/x86/libmono.so", x86_so_path, true);
				}

				Debug.Log("Encrypt libmono.so Success !!");
			}
			else
			{
				Debug.LogError(dllPath+ "  Not Found!!");
			}
		}
	}
}

但是,高兴的别太早。DLL是解不开了,但是你的解密算法是写在.so里面的,那么对方反编译你的.so取出解密算法,随便写个小工具就可以把你的DLL逆向回来。。

在windows上下载Ida Pro神器(真是道高一尺魔高一丈啊)。IdaPro下载连接

然后打开我们编译的libmono.so

找到mono_image_open_from_data_width_name 方法,然后点击F5 解密算法就破解了。


因此我们需要二次加密我们的 .so文件...可以去看雨凇Momo的 Unity3D研究院之Android二次加密.so二次加密DLL(八十二)


2017-05-27 15:06:28 u010707274 阅读数 1053

基于Unity5.0系统的新打包方式

- 列表内容

前言

在使用新的Unity3D 5.0后,我们发现在Inspector面板上多了一个东西



这里主要有两个可以设置的选项AssetBundleVariant

进过测试,发现5.01版本在AssetBundle方面存在bug,幸好在5.02版本进行了修复,所以在5.02以后的版本才可以放心使用,不然会出现贴图丢失等问题




 AssetBundle  在这里指的是我们打包后会生成的包名,可以有后缀,比如ui.assetbundle,ui.uity3d等等,但是我们要注意的是,使用的时候名称要对应起来.

点击菜单里面的New...  我们可以创建出一个新的assetbundle.我们也可以把好几个Prefabs设置 在一个AssetBundle里面,方法很简单,就是选中你要设置的文件,然后

在菜单里面统一设置为你想的名字.比如我的项目里面有好几个元素是打算打包在ui这个包里的,就可以这样设置.




通常随着项目里设置的AssetBundle越来越来,管理起来会比较不方便,比如我想知道ui这个包里面有什么文件的时候,可以点击AssetBundle中的选项,

当然是在当前选项是ui的情况下,点击下面的Filter Selecte Name ,系统就会进行过滤,还有一个最直接的方法,就是在搜索栏里直接输入:



在以往的版本,我们都是使用WWW,这个东西来加载我们打包的资源,接口也是比较简单

  1. public static WWW LoadFromCacheOrDownload(string url, int version);  
  2. public static WWW LoadFromCacheOrDownload(string url, int version, [DefaultValue(“0”)] uint crc);  
    public static WWW LoadFromCacheOrDownload(string url, int version);
    public static WWW LoadFromCacheOrDownload(string url, int version, [DefaultValue("0")] uint crc);
但是在新的5.0版本里,多了两个新的接口

  1. public static WWW LoadFromCacheOrDownload(string url, Hash128 hash);  
  2. public static WWW LoadFromCacheOrDownload(string url, Hash128 hash, [DefaultValue(“0”)] uint crc);  
    public static WWW LoadFromCacheOrDownload(string url, Hash128 hash);
    public static WWW LoadFromCacheOrDownload(string url, Hash128 hash, [DefaultValue("0")] uint crc);
唯一不同的地方就在于Hash128这个东西.关于这个东西官方的api文档中还找不到,经过测试,它的大致用意是用来区分同一个

资源名称,不同版本用的.在新的版本中存在一个叫做AssetBundleManifest,的类,里面封装了如下接口


据官方的介绍,这个类主要是在我们定义的每个AssetBundle的时候会产生一个Manifest文件,它的作用如下:

1.作为CRC校验用

2.Asset文件Hash,将所有的asset资源包含在这个AssetBundle中,但是仅仅用于编译的增量计数检查。

3.资源名称,所有同样名称的AssetBundle都会打进一个包中.通过Mainfest都可以取到这些资源

4.依赖资源的名称.



重点

在这里我觉比较重要的一个东西就是AssetBundle Variant

这个东西主要应用在不同分辨率资源的使用或者多语言图片资源的使用上比较多

例如:当你在不同的分辨率下使用两套不容的资源,在这里我们暂且定义为”texture.hd”和”texture.sd”

我们可以这样做:新建立两个文件夹,叫做”MyAssets HD”和”MyAssets HD”,把他们的AssetBundle统一设置

为”variants/texture”,Variant分为设置为”hd”和”sd”,最终如下:




这样我们就可以通过

  1. public static WWW LoadFromCacheOrDownload(string url, Hash128 hash);  
public static WWW LoadFromCacheOrDownload(string url, Hash128 hash);
的方式来使用我们两种分辨率下的资源了

这里比较关心的是url和hash这两个参数

url,估计大家都知道是”variants/texture”,

然而hash就是需要借助AssetBundleManifest.GetAssetBundleHash的方法来获取了.

今天时间比较匆忙,暂时说到这里,希望可以给大家抛砖引玉.详细的实现方案请看第二讲






















document.getElementById("bdshell_js").src = "http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion=" + Math.ceil(new Date()/3600000) var fromjs = ("#fromjs");
    if (fromjs.length > 0) {
("#fromjs .markdown_views pre").addClass("prettyprint"); prettyPrint(); ('pre.prettyprint code').each(function () {
                var lines =
(this).text().split('\n').length; var numbering= ('
    ').addClass('pre-numbering').hide(); (this).addClass(  hasnumbering  ).parent().append( numbering); for (i = 1; i
2017-11-22 23:21:00 weixin_33834679 阅读数 14

1、Unity3D开发程序员开发好场景后,打包apk包,将此apk包发给android开发人员

2、android开发人员将得到的apk包后缀名改为.zip(即重命名将.apk改为.zip)

3、解压步骤2得到的.zip包,得到文件夹

4、使用Eclipse新建android工程包,打开步骤3中得到的文件夹,将assets文件夹和lib文件夹复制到android工程包目录

5、导入jar包,右键工程属性,java build path-》添加jar到library,Unity安装目录\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar

6、将需要显示Unity3D场景的Activity实现如下,如果需要,请代码添加android自带界面

7、根据Unity3D提供的接口,更换场景,并根据生命周期完成整个项目,指的是除了Unity3D部分

mUnityPlayer = new MyPlayer(this, handler);
 int glesMode = mUnityPlayer.getSettings().getInt("gles_mode", 1);
 boolean trueColor8888 = false;
 mUnityPlayer.init(glesMode, trueColor8888);
 View playerView = mUnityPlayer.getView();

红色部分代码是获取Unity场景界面,拿到界面了,剩下的操作就靠程序员,动态添加在某个布局视图下,或者隐藏,或者。。。






本文转自 glblong 51CTO博客,原文链接:http://blog.51cto.com/glblong/1281758,如需转载请自行联系原作者

没有更多推荐了,返回首页